FFMPEG导出视频测试

This commit is contained in:
Sch 2023-08-10 12:50:29 +08:00
parent 3eb8c9ae88
commit d580c945a6
9 changed files with 144 additions and 37 deletions

BIN
Movie.mp4 Normal file

Binary file not shown.

View File

@ -2,6 +2,11 @@
#include "Cut5/Widgets/DefineGlobal.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
FString FUtils::MakeStringUpright(const FString& String)
{
FString Result;
@ -79,6 +84,91 @@ void FUtils::CreateDefaultTimelineSave(const FString& SavedPath, const FTimeline
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)
{

View File

@ -22,6 +22,7 @@ public:
*/
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)
{

View File

@ -7,6 +7,7 @@ void FMainMenuCommands::RegisterCommands()
UI_COMMAND(OpenProject, "打开", "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(ExportXML, "导出", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(Setting, "设置", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
UI_COMMAND(Exit, "退出", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord());
}

View File

@ -21,6 +21,7 @@ public:
TSharedPtr<FUICommandInfo> OpenProject;
TSharedPtr<FUICommandInfo> SaveProject;
TSharedPtr<FUICommandInfo> SaveProjectAs;
TSharedPtr<FUICommandInfo> ExportXML;
TSharedPtr<FUICommandInfo> Setting;
TSharedPtr<FUICommandInfo> Exit;
};

View File

@ -134,6 +134,7 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent&
// 如果拖拽物是视频,那么对不同轨道进行不同和操作
NewClipData.MoviePath = ClipDragOperation.TimelinePropertyData->MoviePath;
NewClipData.ClipEndFrame = NewClipData.ClipStartFrame + ClipDragOperation.TimelinePropertyData->MovieFrameLength;
NewClipData.VideoEndFrame = ClipDragOperation.TimelinePropertyData->MovieFrameLength;
NewClipData.VideoCapture = ClipDragOperation.VideoCapture;
if (TrackHead->TrackData.TrackType == ETrackType::LightArrayTrack)
{

View File

@ -40,32 +40,8 @@ void SCutMainWindow::Construct(const FArguments& InArgs)
SAssignNew(StatePanel, SStatePanel);
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");
MainBarTextStyle.SetFontSize(17);
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().SaveProject, 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().Exit, NAME_None, FText::FromString(TEXT("退出")));
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")));
// 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()
@ -636,19 +649,20 @@ void SCutMainWindow::OpenProject(const FString& Project)
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);
for (FTrackGroup& TrackGroup : CutTimeline->TrackGroups)
{
for (FTrackData& TrackData : TrackGroup.TrackDataArray)
{
FFileHelper::SaveArrayToFile(Data, *FPaths::Combine(ExportPath, *FGlobalData::CurrentProjectName, FGlobalData::CurrentProjectName + TEXT("_Link.cutlink")));
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())
return;

View File

@ -167,7 +167,6 @@ void STimelineClip::Seek(int32 Frame)
{
case ETrackType::VideoTrack:
{
if (!ClipData->VideoCapture || Frame > ClipData->ClipEndFrame || Frame < 0)
{
return;

BIN
output.mp4 Normal file

Binary file not shown.