diff --git a/Movie.mp4 b/Movie.mp4 new file mode 100644 index 0000000..a91c9a2 Binary files /dev/null and b/Movie.mp4 differ diff --git a/Source/Cut5/Utils/Utils.cpp b/Source/Cut5/Utils/Utils.cpp index 221631a..b8ece19 100644 --- a/Source/Cut5/Utils/Utils.cpp +++ b/Source/Cut5/Utils/Utils.cpp @@ -2,6 +2,11 @@ #include "Cut5/Widgets/DefineGlobal.h" +extern "C" { +#include +#include +} + 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 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(FGlobalData::GlobalFPS);; + int32 EndFrame = (TempClipData.VideoEndFrame) % static_cast(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) { diff --git a/Source/Cut5/Utils/Utils.h b/Source/Cut5/Utils/Utils.h index d6b7bf2..d2bd1fb 100644 --- a/Source/Cut5/Utils/Utils.h +++ b/Source/Cut5/Utils/Utils.h @@ -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) { diff --git a/Source/Cut5/Widgets/Commands/MainMenuCommands.cpp b/Source/Cut5/Widgets/Commands/MainMenuCommands.cpp index b868429..14ad8cf 100644 --- a/Source/Cut5/Widgets/Commands/MainMenuCommands.cpp +++ b/Source/Cut5/Widgets/Commands/MainMenuCommands.cpp @@ -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()); } diff --git a/Source/Cut5/Widgets/Commands/MainMenuCommands.h b/Source/Cut5/Widgets/Commands/MainMenuCommands.h index 04251d9..f580db8 100644 --- a/Source/Cut5/Widgets/Commands/MainMenuCommands.h +++ b/Source/Cut5/Widgets/Commands/MainMenuCommands.h @@ -21,6 +21,7 @@ public: TSharedPtr OpenProject; TSharedPtr SaveProject; TSharedPtr SaveProjectAs; + TSharedPtr ExportXML; TSharedPtr Setting; TSharedPtr Exit; }; \ No newline at end of file diff --git a/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp b/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp index 3d8c535..adf34a6 100644 --- a/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp +++ b/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp @@ -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) { diff --git a/Source/Cut5/Widgets/SCutMainWindow.cpp b/Source/Cut5/Widgets/SCutMainWindow.cpp index 59827b4..483e612 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.cpp +++ b/Source/Cut5/Widgets/SCutMainWindow.cpp @@ -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("NormalText"); MainBarTextStyle.SetFontSize(17); FTextBlockStyle TitleBarTextStyle = FAppStyle::GetWidgetStyle("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 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(CutTimeline->TrackGroupInstances[i].Head)->TrackData, TEXT("D:\\Project\\Cut5\\Movie.mp4")); + } if (ExportPath.IsEmpty()) return; diff --git a/Source/Cut5/Widgets/STimelineClip.cpp b/Source/Cut5/Widgets/STimelineClip.cpp index ed0a25a..67013fb 100644 --- a/Source/Cut5/Widgets/STimelineClip.cpp +++ b/Source/Cut5/Widgets/STimelineClip.cpp @@ -167,7 +167,6 @@ void STimelineClip::Seek(int32 Frame) { case ETrackType::VideoTrack: { - if (!ClipData->VideoCapture || Frame > ClipData->ClipEndFrame || Frame < 0) { return; diff --git a/output.mp4 b/output.mp4 new file mode 100644 index 0000000..bc4bc40 Binary files /dev/null and b/output.mp4 differ