diff --git a/Source/Cut5/Utils/Utils.cpp b/Source/Cut5/Utils/Utils.cpp index 67fac56..1c9dc02 100644 --- a/Source/Cut5/Utils/Utils.cpp +++ b/Source/Cut5/Utils/Utils.cpp @@ -372,46 +372,107 @@ TArray FUtils::TrackEncodeAudio(const FTrackData& TrackData, c { TArray ClipData = TrackData.ClipData; ClipData.Sort([](const FClipData& A, const FClipData& B) {return A.ClipStartFrame < B.ClipStartFrame; }); - - int32 i = 0; - TArray EncodeVideoInfos; - for (FClipData& TempClipData : ClipData) + + // 先拆出所有音频 + int32 AudioCount = 0; { - if (TempClipData.ResourcePropertyDataPtr->Context) + int32 i = 0; + for (FClipData& TempClipData : ClipData) { - FEncodeVideoInfo EncodeVideoInfo; - FTimespan EndTimespan = FTimespan::FromSeconds(TempClipData.VideoEndFrame / FGlobalData::GlobalFPS); - FTimespan StartTimespan = FTimespan::FromSeconds(TempClipData.VideoStartFrame / FGlobalData::GlobalFPS); + if (TempClipData.ResourcePropertyDataPtr->Context) + { + FEncodeVideoInfo EncodeVideoInfo; + 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 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(ExportPath + FString::FromInt(i) + TEXT(".mp3")); + FString OutputFile = FPaths::ConvertRelativePathToFull(GetProjectTempPath() / FString::FromInt(i) + TEXT(".mp3")); - int32 StartFrame = (TempClipData.VideoStartFrame) % static_cast(FGlobalData::GlobalFPS);; - int32 EndFrame = (TempClipData.VideoEndFrame) % static_cast(FGlobalData::GlobalFPS); + int32 StartFrame = (TempClipData.VideoStartFrame) % static_cast(FGlobalData::GlobalFPS);; + int32 EndFrame = (TempClipData.VideoEndFrame) % static_cast(FGlobalData::GlobalFPS); - FString Command = FString::Printf(TEXT("-y -i \"%s\" -ss %s -to %s -c copy \"%s\""), - *InputFile, *StartTime, *EndTime, *OutputFile); + FString Command = FString::Printf(TEXT("-y -i \"%s\" -ss %s -to %s -c copy \"%s\""), + *InputFile, *StartTime, *EndTime, *OutputFile); - FPlatformProcess::CreateProc(*GetFfmepg(), *Command, true, false, false, nullptr, 0, nullptr, nullptr); - - EncodeVideoInfo.EncodedVideoTimeCode = FGlobalData::GetTimeData(TempClipData.ClipStartFrame); - EncodeVideoInfo.EncodedVideoName = ExportPath + FString::FromInt(i) + TEXT(".mp3"); - EncodeVideoInfo.ClipStartFrame = TempClipData.ClipStartFrame; - EncodeVideoInfo.ClipEndFrame = TempClipData.ClipEndFrame; - - EncodeVideoInfo.TrackData = TrackData; - EncodeVideoInfo.ClipData = TempClipData; - EncodeVideoInfos.Add(EncodeVideoInfo); + FPlatformProcess::CreateProc(*GetFfmepg(), *Command, true, false, false, nullptr, 0, nullptr, nullptr); + i++; + AudioCount++; + } } - i++; } + + + FString Header = GetFfmepg(); + for (int32 i = 0; i < ClipData.Num(); i++) + { + Header += " -i "; + Header += "\"" + GetProjectTempPath() / FString::FromInt(i) + ".mp3" + "\""; + } + + Header += " -filter_complex"; + + // [0:0][1:0]...[n:0]concat=n=n=2:v=0:a=1[out] + for (int32 i = 0; i < ClipData.Num(); i++) + { + Header += "[" + FString::FromInt(i) + ":0]"; + } + + Header += "concat=n=" + FString::FromInt(ClipData.Num()) + ":v=0:a=1[out]"; + Header += " -map [out] -y " + ExportPath + ".mp3"; + FPlatformProcess::CreateProc(*GetFfmepg(), *Header, true, false, false, nullptr, 0, nullptr, nullptr); + + + + + + // int32 i = 0; + TArray EncodeVideoInfos; + // for (FClipData& TempClipData : ClipData) + // { + // if (TempClipData.ResourcePropertyDataPtr->Context) + // { + // FEncodeVideoInfo EncodeVideoInfo; + // 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(ExportPath + FString::FromInt(i) + TEXT(".mp3")); + // + // int32 StartFrame = (TempClipData.VideoStartFrame) % static_cast(FGlobalData::GlobalFPS);; + // int32 EndFrame = (TempClipData.VideoEndFrame) % static_cast(FGlobalData::GlobalFPS); + // + // FString Command = FString::Printf(TEXT("-y -i \"%s\" -ss %s -to %s -c copy \"%s\""), + // *InputFile, *StartTime, *EndTime, *OutputFile); + // + // FPlatformProcess::CreateProc(*GetFfmepg(), *Command, true, false, false, nullptr, 0, nullptr, nullptr); + // + // EncodeVideoInfo.EncodedVideoTimeCode = FGlobalData::GetTimeData(TempClipData.ClipStartFrame); + // EncodeVideoInfo.EncodedVideoName = ExportPath + FString::FromInt(i) + TEXT(".mp3"); + // EncodeVideoInfo.ClipStartFrame = TempClipData.ClipStartFrame; + // EncodeVideoInfo.ClipEndFrame = TempClipData.ClipEndFrame; + // + // EncodeVideoInfo.TrackData = TrackData; + // EncodeVideoInfo.ClipData = TempClipData; + // EncodeVideoInfos.Add(EncodeVideoInfo); + // } + // i++; + // } return EncodeVideoInfos; } +TArray FUtils::CombineAudio(const FEncodeVideoInfo& EncodeVideoInfo, + const FEncodeVideoInfo& EncodeAudioInfo, const FString& ExportPath) +{ + return {}; +} + FString FUtils::GetFfmepg() { return FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() / TEXT("Binaries") / TEXT("Win64") / TEXT("ffmpeg.exe")); diff --git a/Source/Cut5/Utils/Utils.h b/Source/Cut5/Utils/Utils.h index ac9fe79..14a4349 100644 --- a/Source/Cut5/Utils/Utils.h +++ b/Source/Cut5/Utils/Utils.h @@ -33,7 +33,8 @@ public: static void CreateDefaultTimelineSave(const FString& SavedPath, const FTimelineInfo::ETimelineType Type); static TArray TrackEncodeVideo(const FTrackData& TrackData, const FString& ExportPath); - static TArray TrackEncodeAudio(const FTrackData& TrackData, const FString& ExportPath); + static TArray TrackEncodeAudio(const FTrackData& TrackDataLeft, const FString& ExportPath); + static TArray CombineAudio(const FEncodeVideoInfo& EncodeVideoInfo, const FEncodeVideoInfo& EncodeAudioInfo, const FString& ExportPath); static FString CurtainFullPath(const FString& GroupName) { // GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FGlobalData::BasePath); diff --git a/Source/Cut5/Widgets/DefineGlobal.h b/Source/Cut5/Widgets/DefineGlobal.h index 79bafdd..6a08379 100644 --- a/Source/Cut5/Widgets/DefineGlobal.h +++ b/Source/Cut5/Widgets/DefineGlobal.h @@ -32,6 +32,7 @@ public: inline static FString CurrentProjectName = "DefaultProject"; inline static FString BasePath = FPaths::ProjectDir(); inline static FString Version = "3.0"; + inline static FString CutVersion = "1.1"; inline static FString ExportPath = ""; inline static TArray Colors = @@ -333,6 +334,7 @@ struct CUT5_API FClipData : public TSharedFromThis Ar << ClipData.MovieBrushesPath; Ar << ClipData.VolumeData; Ar << ClipData.bIsCycle; + Ar << ClipData.AudioCurtains; return Ar; }; @@ -352,6 +354,7 @@ struct CUT5_API FClipData : public TSharedFromThis bool bIsCycle = false; TArray ClipColors; + TArray AudioCurtains; UE_DEPRECATED(0.0, "Use int32 instead of float, Please just use ClipStartFrame and ClipEndFrame") int32 GetClipStartFrame() const { return ClipStartTime / FGlobalData::DefaultTimeTickSpace; }; @@ -1025,7 +1028,7 @@ struct FProperties }; -struct FEncodeVideoInfo +struct FEncodeVideoInfo { FString EncodedVideoName = ""; FString EncodedVideoTimeCode = "00:00:00:00"; diff --git a/Source/Cut5/Widgets/SCutMainWindow.cpp b/Source/Cut5/Widgets/SCutMainWindow.cpp index 53907a1..9d08029 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.cpp +++ b/Source/Cut5/Widgets/SCutMainWindow.cpp @@ -2005,13 +2005,14 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundListElement(tinyxml2::XMLElement* tinyxml2::XMLElement* AudioList = Parent->InsertNewChildElement("SoundList"); { int32 Count = 0; + bool bGlobalAudio = false; for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) { + const FTrackData& TrackData = StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData; if (StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::AudioTrack || StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::AudioTrackR) { FString Filename = GetCurrentSelectFileName(); - if (StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData.TrackType == ETrackType::AudioTrackR) { @@ -2022,16 +2023,48 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundListElement(tinyxml2::XMLElement* Filename += TEXT("_L"); } FString NewExportFilePath = FGlobalData::ExportPath / "Sound" / Filename; - TArray EncodeVideoInfos = FUtils::TrackEncodeAudio(StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData, NewExportFilePath); - for (const FEncodeVideoInfo EncodeVideoInfo : EncodeVideoInfos) + + + bGlobalAudio = false; + FEncodeVideoInfo GlobalAudioEncodeVideoInfo = {}; + for (const FEncodeVideoInfo& EncodeVideoInfo : AllGlobalSounds) { - GetSoundElement(AudioList, EncodeVideoInfo); - Count++; + if (EncodeVideoInfo.ClipData.AudioCurtains.Contains(GetCurrentSelectCurtain())) + { + bGlobalAudio = true; + GlobalAudioEncodeVideoInfo = EncodeVideoInfo; + Count++; + }; + } + + if (bGlobalAudio == false) + { + TArray EncodeVideoInfos = FUtils::TrackEncodeAudio(StaticCastSharedPtr(CutTimeline->TrackGroupInstances[i].Head)->TrackData, NewExportFilePath); + + for (const FEncodeVideoInfo EncodeVideoInfo : EncodeVideoInfos) + { + if (EncodeVideoInfo.ClipData.AudioCurtains.Num() > 0) + { + AllGlobalSounds.Add(EncodeVideoInfo); + } + } + for (const FEncodeVideoInfo EncodeVideoInfo : EncodeVideoInfos) + { + GetSoundElement(AudioList, EncodeVideoInfo); + Count++; + } + } + else + { + GetSoundElement(AudioList, GlobalAudioEncodeVideoInfo); } - } } - if (Count == 0) + + + + + if (Count == 0 && !bGlobalAudio) { GetSoundElement(AudioList, FEncodeVideoInfo()); } @@ -2343,6 +2376,36 @@ tinyxml2::XMLElement* SCutMainWindow::GetGradientLight(tinyxml2::XMLElement* Par return nullptr; } +FGuid SCutMainWindow::GetCurrentSelectCurtain() const +{ + for (FCurtainGroup& CurtainGroup : CurtainPanel->Groups) + { + for (const FCurtain& Curtain : CurtainGroup.Curtains) + { + if (Curtain.bIsActive) + { + return Curtain.CurtainUUID; + } + } + } + return FGuid(); +} + +bool SCutMainWindow::IsSelectCurtain() const +{ + for (FCurtainGroup& CurtainGroup : CurtainPanel->Groups) + { + for (const FCurtain& Curtain : CurtainGroup.Curtains) + { + if (Curtain.bIsActive) + { + return true; + } + } + } + return false; +} + void SCutMainWindow::DeselectAll() { for (FCurtainGroup& CurtainGroup : CurtainPanel->Groups) diff --git a/Source/Cut5/Widgets/SCutMainWindow.h b/Source/Cut5/Widgets/SCutMainWindow.h index 38edfa3..9d6310d 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.h +++ b/Source/Cut5/Widgets/SCutMainWindow.h @@ -111,6 +111,9 @@ public: tinyxml2::XMLElement* GetVideoListElement(tinyxml2::XMLElement* Parent); tinyxml2::XMLElement* GetSoundListElement(tinyxml2::XMLElement* Parent); + + TArray AllGlobalSounds; + tinyxml2::XMLElement* GetProcessA(tinyxml2::XMLElement* Parent, FCurtainGroup* CurtainGroup); tinyxml2::XMLElement* GetProcessB(tinyxml2::XMLElement* Parent, FCurtain* Curtain); tinyxml2::XMLElement* GetSpecialEffectList(tinyxml2::XMLElement* Parent); @@ -121,7 +124,9 @@ public: tinyxml2::XMLElement* GetBreatheLight(tinyxml2::XMLElement* Parent, const FClipData& ClipData); tinyxml2::XMLElement* GetFlashLight(tinyxml2::XMLElement* Parent, const FClipData& ClipData); tinyxml2::XMLElement* GetGradientLight(tinyxml2::XMLElement* Parent, const FClipData& ClipData); - FString GetCurrentSelectFileName(); + FString GetCurrentSelectFileName() const; + FGuid GetCurrentSelectCurtain() const; + bool IsSelectCurtain() const; void DeselectAll(); int32 RotatorSpeakerIndex = 0; @@ -136,7 +141,7 @@ public: void RemoveThread(const FGuid& Guid); }; -inline FString SCutMainWindow::GetCurrentSelectFileName() +inline FString SCutMainWindow::GetCurrentSelectFileName() const { for (FCurtainGroup& CurtainGroup : CurtainPanel->Groups) { diff --git a/Source/Cut5/Widgets/TimelineClips/ClipProxy.cpp b/Source/Cut5/Widgets/TimelineClips/ClipProxy.cpp index 45c515a..04c2afb 100644 --- a/Source/Cut5/Widgets/TimelineClips/ClipProxy.cpp +++ b/Source/Cut5/Widgets/TimelineClips/ClipProxy.cpp @@ -1,6 +1,7 @@ #include "ClipProxy.h" #include "Cut5/Utils/Utils.h" +#include "Cut5/Widgets/SCutMainWindow.h" #include "Cut5/Widgets/SCutTimeline.h" #include "Cut5/Widgets/STimelineClip.h" #include "Cut5/Widgets/MicroWidgets/SNewProjectTips.h" @@ -330,6 +331,101 @@ TSharedPtr FClipProxy::GetPropertiesWidget() ]; } + if (ClipData->ClipType == ETrackType::AudioTrack || ClipData->ClipType == ETrackType::AudioTrackR) + { + AudiosCurtainOptions.Empty(); + TArray& Groups = MainInterface->GetSelf()->CurtainPanel->Groups; + for (FCurtainGroup& Group : Groups) + { + for (FCurtain& Curtain : Group.Curtains) + { + AudiosCurtainOptions.Add(MakeShared(Curtain.CurtainName, Curtain.CurtainUUID)); + } + } + + VerticalBox->AddSlot() + .Padding(0, 16, 0 ,0) + [ + SNew(SBox).HeightOverride(32).WidthOverride(214) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .VAlign(VAlign_Center) + .WidthOverride(62) + .HeightOverride(32) + [ + SNew(STextBlock) + .Text(FText::FromString(TEXT("有效幕"))) + .Font(NormalText.Font) + .Justification(ETextJustify::Center) + ] + ] + + SHorizontalBox::Slot() + .SizeParam(FAuto()) + [ + SNew(SBox) + .WidthOverride(136) + .HeightOverride(32) + [ + SAssignNew(AudiosCurtainComboBox, SComboBox>) + .OptionsSource(&AudiosCurtainOptions) + .OnGenerateWidget_Lambda([this](TSharedPtr InItem) + { + return SNew(SHorizontalBox) + + SHorizontalBox::Slot() + [ + SNew(STextBlock).Text(FText::FromString(*InItem->String)) + ] + + SHorizontalBox::Slot() + .HAlign(HAlign_Right) + [ + SNew(SCheckBox) + .IsChecked(ClipData->AudioCurtains.Contains(*InItem.Get()) ? ECheckBoxState::Checked : ECheckBoxState::Unchecked) + .OnCheckStateChanged_Lambda([this, InItem](const ECheckBoxState& State) + { + if (State == ECheckBoxState::Checked) + { + ClipData->AudioCurtains.Add(*InItem.Get()); + MainInterface->UpdateProperties(this); + } + else + { + ClipData->AudioCurtains.Remove(*InItem.Get()); + MainInterface->UpdateProperties(this); + } + + + }) + ]; + + }) + [ + SNew(STextBlock).Text_Lambda([this]() + { + if (ClipData->AudioCurtains.Num() == 1) + { + return FText::FromString(ClipData->AudioCurtains[0].String); + } + else if (ClipData->AudioCurtains.Num() > 1) + { + return FText::FromString(TEXT("多个")); + } + else + { + return FText::FromString(TEXT("无")); + } + }) + ] + ] + ] + ] + ]; + } + return PropertiesWidget; diff --git a/Source/Cut5/Widgets/TimelineClips/ClipProxy.h b/Source/Cut5/Widgets/TimelineClips/ClipProxy.h index 1256aba..654ca6b 100644 --- a/Source/Cut5/Widgets/TimelineClips/ClipProxy.h +++ b/Source/Cut5/Widgets/TimelineClips/ClipProxy.h @@ -10,9 +10,11 @@ public: void UpdateInterface(TSharedPtr InTimeClip); FSlateColor GetColor() const; virtual TSharedPtr GetPropertiesWidget() override; - - void Reset(); + TArray> AudiosCurtainOptions; TSharedPtr>> GroupComboBox; + TSharedPtr>> AudiosCurtainComboBox; + void Reset(); + TArray> Selectable; TSharedPtr TimelineClip = nullptr;