FFMPEG导出视频测试
This commit is contained in:
parent
3eb8c9ae88
commit
d580c945a6
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
#include "Cut5/Widgets/DefineGlobal.h"
|
#include "Cut5/Widgets/DefineGlobal.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
}
|
||||||
|
|
||||||
FString FUtils::MakeStringUpright(const FString& String)
|
FString FUtils::MakeStringUpright(const FString& String)
|
||||||
{
|
{
|
||||||
FString Result;
|
FString Result;
|
||||||
@ -79,6 +84,91 @@ void FUtils::CreateDefaultTimelineSave(const FString& SavedPath, const FTimeline
|
|||||||
FFileHelper::SaveArrayToFile(SavedData, *SavedPath);
|
FFileHelper::SaveArrayToFile(SavedData, *SavedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FUtils::TrackEncodeVideo(const FTrackData& TrackData, const FString& ExportPath)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (TrackData.ClipData.Num() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AVFormatContext *outctx = NULL;
|
||||||
|
AVStream *outstream = NULL;
|
||||||
|
AVCodecContext *outcodecctx = NULL;
|
||||||
|
AVCodec *outcodec = NULL;
|
||||||
|
|
||||||
|
avformat_alloc_output_context2(&outctx, NULL, NULL, "output.mp4");
|
||||||
|
outcodec = avcodec_find_encoder(AV_CODEC_ID_H264);
|
||||||
|
outstream = avformat_new_stream(outctx, outcodec);
|
||||||
|
outcodecctx = avcodec_alloc_context3(outcodec);
|
||||||
|
|
||||||
|
|
||||||
|
TArray<FClipData> ClipData = TrackData.ClipData;
|
||||||
|
ClipData.Sort([](const FClipData& A, const FClipData& B) {return A.ClipStartFrame < B.ClipStartFrame; });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 对于每个AVFrame
|
||||||
|
AVFrame *frame = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (FClipData& TempClipData : ClipData)
|
||||||
|
{
|
||||||
|
// 先从 ClipData 中读取这个 Clip 的视频数据。
|
||||||
|
if (TempClipData.ResourcePropertyDataPtr->Context)
|
||||||
|
{
|
||||||
|
FTimespan EndTimespan = FTimespan::FromSeconds(TempClipData.VideoEndFrame / FGlobalData::GlobalFPS);
|
||||||
|
FTimespan StartTimespan = FTimespan::FromSeconds(TempClipData.VideoStartFrame / FGlobalData::GlobalFPS);
|
||||||
|
|
||||||
|
|
||||||
|
FString StartTime = FString::Printf(TEXT("%02d:%02d:%02d"), StartTimespan.GetHours(), StartTimespan.GetMinutes(), StartTimespan.GetSeconds());
|
||||||
|
FString EndTime = FString::Printf(TEXT("%02d:%02d:%02d"), EndTimespan.GetHours(), EndTimespan.GetMinutes(), EndTimespan.GetSeconds());
|
||||||
|
FString InputFile = TempClipData.ResourcePropertyDataPtr->MoviePath;
|
||||||
|
FString OutputFile = FPaths::ConvertRelativePathToFull((FPaths::Combine(FPaths::ProjectDir(), TEXT("output.mp4"))));
|
||||||
|
int32 StartFrame = (TempClipData.VideoStartFrame) % static_cast<int>(FGlobalData::GlobalFPS);;
|
||||||
|
int32 EndFrame = (TempClipData.VideoEndFrame) % static_cast<int>(FGlobalData::GlobalFPS);
|
||||||
|
|
||||||
|
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FString::Printf(TEXT("StartFrame: %d, EndFrame: %d"), StartFrame, EndFrame));
|
||||||
|
//输出文件名字
|
||||||
|
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FString::Printf(TEXT("OutputFile: %s"), *OutputFile));
|
||||||
|
//输入文件名字
|
||||||
|
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FString::Printf(TEXT("InputFile: %s"), *InputFile));
|
||||||
|
//开始时间
|
||||||
|
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FString::Printf(TEXT("StartTime: %s"), *StartTime));
|
||||||
|
//结束时间
|
||||||
|
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FString::Printf(TEXT("EndTime: %s"), *EndTime));
|
||||||
|
// 构造FFmpeg命令
|
||||||
|
FString Command = FString::Printf(TEXT("-y -i %s -ss %s -to %s -c copy %s"),
|
||||||
|
*InputFile, *StartTime, *EndTime, *OutputFile);
|
||||||
|
|
||||||
|
FString FFmpegPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() / TEXT("Binaries") / TEXT("Win64") / TEXT("ffmpeg.exe"));
|
||||||
|
GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Red, FFmpegPath + " " + FString::Printf(TEXT("%s"), *Command));
|
||||||
|
// FFmpeg的路径,需要根据实际情况修改
|
||||||
|
|
||||||
|
|
||||||
|
// 创建进程
|
||||||
|
FPlatformProcess::CreateProc(*FFmpegPath, *Command, true, false, false, nullptr, 0, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// av_write_trailer(outctx);
|
||||||
|
// avio_close(outctx->pb);
|
||||||
|
// avformat_free_context(outctx);
|
||||||
|
// av_frame_free(&frame);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
FSaveModifier::FSaveModifier(const FString& FullSavedPath)
|
FSaveModifier::FSaveModifier(const FString& FullSavedPath)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
static void CreateDefaultTimelineSave(const FString& SavedPath, const FTimelineInfo::ETimelineType Type);
|
static void CreateDefaultTimelineSave(const FString& SavedPath, const FTimelineInfo::ETimelineType Type);
|
||||||
|
|
||||||
|
static void TrackEncodeVideo(const FTrackData& TrackData, const FString& ExportPath);
|
||||||
|
|
||||||
static FString GroupFullPath(const FString& GroupName)
|
static FString GroupFullPath(const FString& GroupName)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ void FMainMenuCommands::RegisterCommands()
|
|||||||
UI_COMMAND(OpenProject, "打开", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
UI_COMMAND(OpenProject, "打开", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
||||||
UI_COMMAND(SaveProject, "保存", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
UI_COMMAND(SaveProject, "保存", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
||||||
UI_COMMAND(SaveProjectAs, "另存为", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
UI_COMMAND(SaveProjectAs, "另存为", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
||||||
|
UI_COMMAND(ExportXML, "导出", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
||||||
UI_COMMAND(Setting, "设置", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
UI_COMMAND(Setting, "设置", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
||||||
UI_COMMAND(Exit, "退出", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
UI_COMMAND(Exit, "退出", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ public:
|
|||||||
TSharedPtr<FUICommandInfo> OpenProject;
|
TSharedPtr<FUICommandInfo> OpenProject;
|
||||||
TSharedPtr<FUICommandInfo> SaveProject;
|
TSharedPtr<FUICommandInfo> SaveProject;
|
||||||
TSharedPtr<FUICommandInfo> SaveProjectAs;
|
TSharedPtr<FUICommandInfo> SaveProjectAs;
|
||||||
|
TSharedPtr<FUICommandInfo> ExportXML;
|
||||||
TSharedPtr<FUICommandInfo> Setting;
|
TSharedPtr<FUICommandInfo> Setting;
|
||||||
TSharedPtr<FUICommandInfo> Exit;
|
TSharedPtr<FUICommandInfo> Exit;
|
||||||
};
|
};
|
@ -134,6 +134,7 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent&
|
|||||||
// 如果拖拽物是视频,那么对不同轨道进行不同和操作
|
// 如果拖拽物是视频,那么对不同轨道进行不同和操作
|
||||||
NewClipData.MoviePath = ClipDragOperation.TimelinePropertyData->MoviePath;
|
NewClipData.MoviePath = ClipDragOperation.TimelinePropertyData->MoviePath;
|
||||||
NewClipData.ClipEndFrame = NewClipData.ClipStartFrame + ClipDragOperation.TimelinePropertyData->MovieFrameLength;
|
NewClipData.ClipEndFrame = NewClipData.ClipStartFrame + ClipDragOperation.TimelinePropertyData->MovieFrameLength;
|
||||||
|
NewClipData.VideoEndFrame = ClipDragOperation.TimelinePropertyData->MovieFrameLength;
|
||||||
NewClipData.VideoCapture = ClipDragOperation.VideoCapture;
|
NewClipData.VideoCapture = ClipDragOperation.VideoCapture;
|
||||||
if (TrackHead->TrackData.TrackType == ETrackType::LightArrayTrack)
|
if (TrackHead->TrackData.TrackType == ETrackType::LightArrayTrack)
|
||||||
{
|
{
|
||||||
|
@ -40,32 +40,8 @@ void SCutMainWindow::Construct(const FArguments& InArgs)
|
|||||||
SAssignNew(StatePanel, SStatePanel);
|
SAssignNew(StatePanel, SStatePanel);
|
||||||
SAssignNew(CustomPanel, SCustomPanel);
|
SAssignNew(CustomPanel, SCustomPanel);
|
||||||
|
|
||||||
FMainMenuCommands::Register();
|
|
||||||
CommandList = MakeShareable(new FUICommandList);
|
|
||||||
CommandList->MapAction(FMainMenuCommands::Get().NewProject, FExecuteAction::CreateLambda([this]()
|
|
||||||
{
|
|
||||||
PreNewProject();
|
|
||||||
}));
|
|
||||||
CommandList->MapAction(FMainMenuCommands::Get().OpenProject, FExecuteAction::CreateLambda([this]()
|
|
||||||
{
|
|
||||||
FString String;
|
|
||||||
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
|
||||||
DesktopPlatform->OpenDirectoryDialog(nullptr, TEXT("选择新建路径"), String, String);
|
|
||||||
if (String.IsEmpty())
|
|
||||||
return;
|
|
||||||
OpenProject(String);
|
|
||||||
}));
|
|
||||||
CommandList->MapAction(FMainMenuCommands::Get().SaveProject, FExecuteAction::CreateLambda([this]()
|
|
||||||
{
|
|
||||||
SaveProject();
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
CommandList->MapAction(FMainMenuCommands::Get().Exit, FExecuteAction::CreateLambda([this]()
|
|
||||||
{
|
|
||||||
FPlatformMisc::RequestExit(true);
|
|
||||||
}));
|
|
||||||
|
|
||||||
FTextBlockStyle MainBarTextStyle = FAppStyle::GetWidgetStyle<FTextBlockStyle>("NormalText");
|
FTextBlockStyle MainBarTextStyle = FAppStyle::GetWidgetStyle<FTextBlockStyle>("NormalText");
|
||||||
MainBarTextStyle.SetFontSize(17);
|
MainBarTextStyle.SetFontSize(17);
|
||||||
FTextBlockStyle TitleBarTextStyle = FAppStyle::GetWidgetStyle<FTextBlockStyle>("NormalText");
|
FTextBlockStyle TitleBarTextStyle = FAppStyle::GetWidgetStyle<FTextBlockStyle>("NormalText");
|
||||||
@ -131,6 +107,7 @@ void SCutMainWindow::Construct(const FArguments& InArgs)
|
|||||||
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().OpenProject, NAME_None, FText::FromString(TEXT("打开")));
|
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().OpenProject, NAME_None, FText::FromString(TEXT("打开")));
|
||||||
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().SaveProject, NAME_None, FText::FromString(TEXT("保存")));
|
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().SaveProject, NAME_None, FText::FromString(TEXT("保存")));
|
||||||
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().SaveProjectAs, NAME_None, FText::FromString(TEXT("保存为")));
|
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().SaveProjectAs, NAME_None, FText::FromString(TEXT("保存为")));
|
||||||
|
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().ExportXML, NAME_None, FText::FromString(TEXT("导出项目到xml")));
|
||||||
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().Setting, NAME_None, FText::FromString(TEXT("设置")));
|
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().Setting, NAME_None, FText::FromString(TEXT("设置")));
|
||||||
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().Exit, NAME_None, FText::FromString(TEXT("退出")));
|
MenuBuilder.AddMenuEntry(FMainMenuCommands::Get().Exit, NAME_None, FText::FromString(TEXT("退出")));
|
||||||
FSlateApplication::Get().PushMenu(AsShared(), FWidgetPath(), MenuBuilder.MakeWidget(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu));
|
FSlateApplication::Get().PushMenu(AsShared(), FWidgetPath(), MenuBuilder.MakeWidget(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu));
|
||||||
@ -350,6 +327,42 @@ void SCutMainWindow::Construct(const FArguments& InArgs)
|
|||||||
// OpenProject(FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("DefaultProject")));
|
// OpenProject(FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("DefaultProject")));
|
||||||
// ImportProject("");
|
// ImportProject("");
|
||||||
|
|
||||||
|
|
||||||
|
FMainMenuCommands::Register();
|
||||||
|
CommandList = MakeShareable(new FUICommandList);
|
||||||
|
CommandList->MapAction(FMainMenuCommands::Get().ExportXML, FExecuteAction::CreateLambda([this]()
|
||||||
|
{
|
||||||
|
FString String;
|
||||||
|
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
||||||
|
DesktopPlatform->OpenDirectoryDialog(nullptr, TEXT("选择导出路径"), String, String);
|
||||||
|
if (String.IsEmpty())
|
||||||
|
return;
|
||||||
|
ExportProject(String);
|
||||||
|
}));
|
||||||
|
|
||||||
|
CommandList->MapAction(FMainMenuCommands::Get().NewProject, FExecuteAction::CreateLambda([this]()
|
||||||
|
{
|
||||||
|
PreNewProject();
|
||||||
|
}));
|
||||||
|
CommandList->MapAction(FMainMenuCommands::Get().OpenProject, FExecuteAction::CreateLambda([this]()
|
||||||
|
{
|
||||||
|
FString String;
|
||||||
|
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
|
||||||
|
DesktopPlatform->OpenDirectoryDialog(nullptr, TEXT("选择新建路径"), String, String);
|
||||||
|
if (String.IsEmpty())
|
||||||
|
return;
|
||||||
|
OpenProject(String);
|
||||||
|
}));
|
||||||
|
CommandList->MapAction(FMainMenuCommands::Get().SaveProject, FExecuteAction::CreateLambda([this]()
|
||||||
|
{
|
||||||
|
SaveProject();
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// CommandList->MapAction(FMainMenuCommands::Get().Exit, FExecuteAction::CreateLambda([this]()
|
||||||
|
// {
|
||||||
|
// FPlatformMisc::RequestExit(true);
|
||||||
|
// }));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SCutMainWindow::Render()
|
void SCutMainWindow::Render()
|
||||||
@ -636,19 +649,20 @@ void SCutMainWindow::OpenProject(const FString& Project)
|
|||||||
|
|
||||||
void SCutMainWindow::ExportProject(const FString& ExportPath)
|
void SCutMainWindow::ExportProject(const FString& ExportPath)
|
||||||
{
|
{
|
||||||
// Save Project Link
|
|
||||||
TArray<uint8> Data {0};
|
|
||||||
FMemoryWriter MemoryWriter(Data);
|
|
||||||
FString CustomInputPanelSavePath = ExportPath + "/" + FGlobalData::CurrentProjectName + "/Dataset/" + TEXT("CustomInputPanel.bin");
|
|
||||||
MemoryWriter << CustomInputPanelSavePath;
|
|
||||||
CustomInputPanel->SavePanel(CustomInputPanelSavePath);
|
|
||||||
|
|
||||||
FString FXPath = ExportPath + "/" + FGlobalData::CurrentProjectName + "/Dataset/" + TEXT("FX.bin");
|
|
||||||
MemoryWriter << FXPath;
|
|
||||||
EffectCardsPanel->SavePanel(FXPath);
|
|
||||||
|
|
||||||
FFileHelper::SaveArrayToFile(Data, *FPaths::Combine(ExportPath, *FGlobalData::CurrentProjectName, FGlobalData::CurrentProjectName + TEXT("_Link.cutlink")));
|
|
||||||
|
|
||||||
|
for (FTrackGroup& TrackGroup : CutTimeline->TrackGroups)
|
||||||
|
{
|
||||||
|
for (FTrackData& TrackData : TrackGroup.TrackDataArray)
|
||||||
|
{
|
||||||
|
|
||||||
|
FUtils::TrackEncodeVideo(TrackData, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++)
|
||||||
|
{
|
||||||
|
FUtils::TrackEncodeVideo(StaticCastSharedPtr<STrackHead>(CutTimeline->TrackGroupInstances[i].Head)->TrackData, TEXT("D:\\Project\\Cut5\\Movie.mp4"));
|
||||||
|
}
|
||||||
|
|
||||||
if (ExportPath.IsEmpty())
|
if (ExportPath.IsEmpty())
|
||||||
return;
|
return;
|
||||||
|
@ -167,7 +167,6 @@ void STimelineClip::Seek(int32 Frame)
|
|||||||
{
|
{
|
||||||
case ETrackType::VideoTrack:
|
case ETrackType::VideoTrack:
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!ClipData->VideoCapture || Frame > ClipData->ClipEndFrame || Frame < 0)
|
if (!ClipData->VideoCapture || Frame > ClipData->ClipEndFrame || Frame < 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
BIN
output.mp4
Normal file
BIN
output.mp4
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user