From 69ba93ba53f1fb40fada9ae4b8d75ba1b374faea Mon Sep 17 00:00:00 2001 From: Sch <3516520171@qq.com> Date: Wed, 23 Aug 2023 20:56:11 +0800 Subject: [PATCH] 8.23 - 02 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.删除轨道后 轨道组没有对齐 O 2. 纹理回收后导致崩溃 O 3. 拖拽预设会根据当前条长度变化 O 4. 轨道复制片段到其他轨道 O 5. SCutmaininterface 577行报错 O 6. 时间轴自己分离移动了 O 7. 加一个移动时间轴的时候显示时间码功能 O 8. 时间轴tick离近了会取消显示一部分数字 O 9. 音频播放不了 O 10. 幕拖动和上下移 O 11. 片段点阵播放不了 O --- Source/Cut5/Utils/FFMPEGUtils.cpp | 249 +++++++++++++----- Source/Cut5/Utils/FFMPEGUtils.h | 3 + .../Cut5/Widgets/Commands/ShortcunCommands.h | 6 +- .../Widgets/Commands/ShortcutCommands.cpp | 6 + Source/Cut5/Widgets/Curtain/SCurtainPanel.cpp | 74 +++++- Source/Cut5/Widgets/Curtain/SCurtainPanel.h | 4 + Source/Cut5/Widgets/DefineGlobal.h | 5 +- .../DragDropOperator/DragDropOperator.cpp | 12 +- Source/Cut5/Widgets/SCutMainWindow.cpp | 108 ++++++-- Source/Cut5/Widgets/SCutMainWindow.h | 6 +- Source/Cut5/Widgets/SCutTimeline.cpp | 13 +- Source/Cut5/Widgets/SCutTimeline.h | 3 +- Source/Cut5/Widgets/STimelineClip.cpp | 208 +++++++++++---- Source/Cut5/Widgets/STimelineClip.h | 8 +- Source/Cut5/Widgets/STimelineTick.cpp | 6 +- Source/Cut5/Widgets/STrackHead.cpp | 4 +- .../Cut5/Widgets/StatePanel/SVideoPlayer.cpp | 13 +- Source/Cut5/Widgets/StatePanel/SVideoPlayer.h | 4 +- 18 files changed, 553 insertions(+), 179 deletions(-) diff --git a/Source/Cut5/Utils/FFMPEGUtils.cpp b/Source/Cut5/Utils/FFMPEGUtils.cpp index 82d9f90..cf016ff 100644 --- a/Source/Cut5/Utils/FFMPEGUtils.cpp +++ b/Source/Cut5/Utils/FFMPEGUtils.cpp @@ -1,4 +1,5 @@ #include "FFMPEGUtils.h" +#include "FFMPEGUtils.h" #include "CanvasTypes.h" #include "ImageUtils.h" @@ -21,7 +22,7 @@ FString FFFMPEGUtils::LoadMedia(const FString& Path, FTimelinePropertyData* Prop { return TEXT("Failed"); } - PropertyData->MovieFrameLength = FormatContext->duration / AV_TIME_BASE * 30; + PropertyData->MovieFrameLength = FormatContext->duration / AV_TIME_BASE * FGlobalData::GlobalFPS; int32 VideoStream = -1; int32 AudioStream = -1; for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { @@ -92,6 +93,7 @@ FString FFFMPEGUtils::LoadMedia(const FString& Path, FTimelinePropertyData* Prop FGuid Guid = FGuid::NewGuid(); ExportImage(Texture, *FPaths::Combine(FGlobalData::BasePath, FGlobalData::CurrentProjectName, "Resources", "Thumbnail", Guid.ToString() + ".png")); PropertyData->IconPath = FPaths::Combine(FGlobalData::BasePath, FGlobalData::CurrentProjectName, "Resources", "Thumbnail", Guid.ToString() + ".png"); + Texture->MarkAsGarbage(); delete RawData; } } @@ -168,6 +170,75 @@ FString FFFMPEGUtils::LoadMedia(const FString& Path, FTimelinePropertyData* Prop return {}; } +FString FFFMPEGUtils::LoadContextPure(const FString& Path, FTimelinePropertyData* PropertyData) +{ + AVFormatContext* FormatContext = nullptr; + if (avformat_open_input(&FormatContext, TCHAR_TO_UTF8(*Path), nullptr, nullptr) != 0) + { + return TEXT("Failed"); + } + if (avformat_find_stream_info(FormatContext, nullptr) < 0) + { + return TEXT("Failed"); + } + PropertyData->MovieFrameLength = FormatContext->duration / AV_TIME_BASE * FGlobalData::GlobalFPS; + int32 VideoStream = -1; + int32 AudioStream = -1; + for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { + if (FormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + VideoStream = i; + } else if (FormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + AudioStream = i; + } + } + + if (VideoStream != -1) + { + AVCodecContext* VideoCodecContext = avcodec_alloc_context3(nullptr); + avcodec_parameters_to_context(VideoCodecContext, FormatContext->streams[VideoStream]->codecpar); + AVCodec* VideoCodec = avcodec_find_decoder(VideoCodecContext->codec_id); + + VideoCodecContext->time_base = AVRational({1, 30}); + VideoCodecContext->gop_size = 1; + + if (avcodec_open2(VideoCodecContext, VideoCodec, nullptr) < 0) + { + return TEXT("Failed"); + } + PropertyData->VideoCodec = VideoCodec; + PropertyData->VideoCodecContext = VideoCodecContext; + } + + if (AudioStream != -1) + { + AVCodecContext* AudioCodecContext = avcodec_alloc_context3(nullptr); + avcodec_parameters_to_context(AudioCodecContext, FormatContext->streams[AudioStream]->codecpar); + AVCodec* AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id); + if (avcodec_open2(AudioCodecContext, AudioCodec, nullptr) < 0) + { + return TEXT("Failed"); + } + PropertyData->AudioCodecContext = AudioCodecContext; + PropertyData->AudioCodec = AudioCodec; + PropertyData->AudioSample = AudioCodecContext->sample_rate; + PropertyData->AudioChannels = AudioCodecContext->channels; + } + PropertyData->VideoStream = VideoStream; + PropertyData->AudioStream = AudioStream; + PropertyData->Context = FormatContext; + if (VideoStream != -1) + { + PropertyData->Type = ETrackType::VideoTrack; + } + else if (AudioStream != -1) + { + PropertyData->Type = ETrackType::AudioTrack; + } + PropertyData->Name = FPaths::GetBaseFilename(Path); + PropertyData->MoviePath = Path; + return {}; +} + FString FFFMPEGUtils::ConvertMediaGoPto1(const FString& Path) { AVFormatContext* FormatContext = nullptr; @@ -239,18 +310,17 @@ bool FFFMPEGUtils::ExportImage(UTexture2D* Texture2D, const FString& Path) TArray FFFMPEGUtils::GetMovieBrush(FClipData* ClipData, bool Regenerate) { - TArray Result; ClipData->MovieBrushes.Empty(); + ClipData->MovieBrushNum = 0; if (ClipData->MovieBrushesPath.Num() > 0 && !Regenerate) { for (int32 i = 0; i < ClipData->MovieBrushesPath.Num(); i++) { FSlateDynamicImageBrush Brush = FSlateDynamicImageBrush(*ClipData->MovieBrushesPath[i], FVector2f(0, 0)); + ClipData->MovieBrushNum++; Result.Add(Brush); } - - return Result; } @@ -279,68 +349,68 @@ TArray FFFMPEGUtils::GetMovieBrush(FClipData* ClipData, bool Regene for (int32 i = 0; i < FrameNum; i++) { // 得到视频总帧数 - - int64 Timestamp = av_rescale_q((i * (TotalFrame / 128) + ClipData->VideoStartFrame) / 30.0 * AV_TIME_BASE, AVRational{1, AV_TIME_BASE}, ClipData->ResourcePropertyDataPtr->Context->streams[ClipData->ResourcePropertyDataPtr->VideoStream]->time_base); - av_seek_frame(ClipData->ResourcePropertyDataPtr->Context, ClipData->ResourcePropertyDataPtr->VideoStream, Timestamp, AVSEEK_FLAG_BACKWARD); - AVPacket Packet; - av_init_packet(&Packet); - AVFormatContext* FormatContext = ClipData->ResourcePropertyDataPtr->Context; - AVCodecContext* VideoCodecContext = ClipData->ResourcePropertyDataPtr->VideoCodecContext; - AVCodec* VideoCodec = ClipData->ResourcePropertyDataPtr->VideoCodec; - AVFrame* Frame = av_frame_alloc(); - - while (av_read_frame(FormatContext, &Packet) >= 0) - { - if (avcodec_send_packet(VideoCodecContext, &Packet) < 0) - { - - } - if (avcodec_receive_frame(VideoCodecContext, Frame) >= 0) - { - break; - } - }; - - - - - if (Frame) - { - struct SwsContext* swsCtx = sws_getContext( - Frame->width, Frame->height, VideoCodecContext->pix_fmt, - Frame->width / 10, Frame->height / 10, AV_PIX_FMT_BGRA, - SWS_BILINEAR, NULL, NULL, NULL - ); - if (!swsCtx) - { - UE_LOG(LogTemp, Error, TEXT("Error creating swsContext")); - } - uint8* RawData = new uint8[(Frame->width / 10) * (Frame->height / 10) * 4]; - uint8* dest[4] = {RawData, 0, 0, 0}; - int32 dest_linesize[4] = {(Frame->width / 10) * 4, 0, 0, 0}; - sws_scale(swsCtx, Frame->data, Frame->linesize, 0, Frame->height, dest, dest_linesize); - sws_freeContext(swsCtx); - - - UTexture2D* Texture = UTexture2D::CreateTransient(Frame->width / 10, Frame->height / 10, PF_B8G8R8A8); - if (Texture) - { - void* MipData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(MipData, RawData, (Frame->width / 10) * (Frame->height / 10) * 4); - Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); - Texture->UpdateResource(); - - FGuid Guid = FGuid::NewGuid(); - ExportImage(Texture, *FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png")); - ClipData->MovieBrushesPath.Add(FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png")); - FSlateDynamicImageBrush Brush = FSlateDynamicImageBrush(*ClipData->MovieBrushesPath[i], FVector2f(0, 0)); - Result.Add(Brush); - - delete RawData; - } - - - } + ClipData->MovieBrushNum ++; + // int64 Timestamp = av_rescale_q((i * (TotalFrame / 128) + ClipData->VideoStartFrame) / 30.0 * AV_TIME_BASE, AVRational{1, AV_TIME_BASE}, ClipData->ResourcePropertyDataPtr->Context->streams[ClipData->ResourcePropertyDataPtr->VideoStream]->time_base); + // av_seek_frame(ClipData->ResourcePropertyDataPtr->Context, ClipData->ResourcePropertyDataPtr->VideoStream, Timestamp, AVSEEK_FLAG_BACKWARD); + // AVPacket Packet; + // av_init_packet(&Packet); + // AVFormatContext* FormatContext = ClipData->ResourcePropertyDataPtr->Context; + // AVCodecContext* VideoCodecContext = ClipData->ResourcePropertyDataPtr->VideoCodecContext; + // AVCodec* VideoCodec = ClipData->ResourcePropertyDataPtr->VideoCodec; + // AVFrame* Frame = av_frame_alloc(); + // + // while (av_read_frame(FormatContext, &Packet) >= 0) + // { + // if (avcodec_send_packet(VideoCodecContext, &Packet) < 0) + // { + // + // } + // if (avcodec_receive_frame(VideoCodecContext, Frame) >= 0) + // { + // break; + // } + // }; + // + // + // + // + // if (Frame) + // { + // struct SwsContext* swsCtx = sws_getContext( + // Frame->width, Frame->height, VideoCodecContext->pix_fmt, + // Frame->width / 10, Frame->height / 10, AV_PIX_FMT_BGRA, + // SWS_BILINEAR, NULL, NULL, NULL + // ); + // if (!swsCtx) + // { + // UE_LOG(LogTemp, Error, TEXT("Error creating swsContext")); + // } + // uint8* RawData = new uint8[(Frame->width / 10) * (Frame->height / 10) * 4]; + // uint8* dest[4] = {RawData, 0, 0, 0}; + // int32 dest_linesize[4] = {(Frame->width / 10) * 4, 0, 0, 0}; + // sws_scale(swsCtx, Frame->data, Frame->linesize, 0, Frame->height, dest, dest_linesize); + // sws_freeContext(swsCtx); + // + // + // UTexture2D* Texture = UTexture2D::CreateTransient(Frame->width / 10, Frame->height / 10, PF_B8G8R8A8); + // if (Texture) + // { + // void* MipData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + // FMemory::Memcpy(MipData, RawData, (Frame->width / 10) * (Frame->height / 10) * 4); + // Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + // Texture->UpdateResource(); + // + // FGuid Guid = FGuid::NewGuid(); + // ExportImage(Texture, *FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png")); + // ClipData->MovieBrushesPath.Add(FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png")); + // FSlateDynamicImageBrush Brush = FSlateDynamicImageBrush(*ClipData->MovieBrushesPath[i], FVector2f(0, 0)); + // Result.Add(Brush); + // + // delete RawData; + // } + // + // + // } } } return Result; @@ -470,7 +540,7 @@ TArray FFFMPEGUtils::GetAudioBrush(FClipData* ClipData) FBufferArchive Buffer; FImageUtils::ExportRenderTarget2DAsPNG(TextureRenderTarget2D, Buffer); FFileHelper::SaveArrayToFile(Buffer, *FPaths::Combine(FUtils::GetTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png")); - + TextureRenderTarget2D->MarkAsGarbage(); FSlateDynamicImageBrush SlateBrush = FSlateDynamicImageBrush(*FPaths::Combine(FUtils::GetTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png"), FVector2D(PicLength, FGlobalData::DefaultTrackHeight)); ClipData->AudioBrushLength.Add(PicLength); ClipData->AudioBrushes.Add(SlateBrush); @@ -488,3 +558,48 @@ TArray FFFMPEGUtils::GetAudioBrush(FClipData* ClipData) return {}; } + + +TArray> FFFMPEGUtils::GetVideoFrameLightArray(FString VideoPath, int32 X, int32 Y) +{ + FTimelinePropertyData TimelinePropertyData; + LoadContextPure(VideoPath, &TimelinePropertyData); + TArray> Colors; + if (TimelinePropertyData.Context == nullptr) + return {}; + + AVPacket* Packet = av_packet_alloc(); + AVFrame* Frame = av_frame_alloc(); + while (1) + { + TArray Color; + if (av_read_frame(TimelinePropertyData.Context, Packet) < 0) + { + if (av_read_frame(TimelinePropertyData.Context, Packet) < 0) + { + break; + } + } + avcodec_send_packet(TimelinePropertyData.VideoCodecContext, Packet); + if (avcodec_receive_frame(TimelinePropertyData.VideoCodecContext, Frame) >= 0) + { + if (SwsContext* SwsContext = sws_getContext( + Frame->width, Frame->height, TimelinePropertyData.VideoCodecContext->pix_fmt, + FGlobalData::LightArrayX, FGlobalData::LightArrayY, AV_PIX_FMT_RGBA, + SWS_BILINEAR, nullptr, nullptr, nullptr)) + { + uint8* RawData = new uint8[FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4]; + uint8* Dest[4] = {RawData, nullptr, nullptr, nullptr}; + int32 DestLineSize[4] = {FGlobalData::LightArrayX * 4, 0, 0, 0}; + sws_scale(SwsContext, Frame->data, Frame->linesize, 0, Frame->height, Dest, DestLineSize); + sws_freeContext(SwsContext); + for (int i = 0; i < FGlobalData::LightArrayX * FGlobalData::LightArrayY; i++) + { + Color.Add(FColor(RawData[i * 4 + 0], RawData[i * 4 + 1], RawData[i * 4 + 2], RawData[i * 4 + 3])); + } + Colors.Add(Color); + } + } + } + return Colors; +} \ No newline at end of file diff --git a/Source/Cut5/Utils/FFMPEGUtils.h b/Source/Cut5/Utils/FFMPEGUtils.h index f5f97fc..47c5018 100644 --- a/Source/Cut5/Utils/FFMPEGUtils.h +++ b/Source/Cut5/Utils/FFMPEGUtils.h @@ -14,8 +14,11 @@ struct FFFMPEGUtils * @return Return Paths */ static FString LoadMedia(const FString& Path, FTimelinePropertyData* PropertyData); + static FString LoadContextPure(const FString& Path, FTimelinePropertyData* PropertyData); static FString ConvertMediaGoPto1(const FString& Path); static bool ExportImage(UTexture2D* Texture2D, const FString& Path); + static TArray> GetVideoFrameLightArray(FString VideoPath, int32 X, int32 Y); + static TArray GetMovieBrush(struct FClipData* ClipData, bool Regenerate = false); static TArray GetAudioBrush(struct FClipData* ClipData); diff --git a/Source/Cut5/Widgets/Commands/ShortcunCommands.h b/Source/Cut5/Widgets/Commands/ShortcunCommands.h index 8a35fe0..c869ce7 100644 --- a/Source/Cut5/Widgets/Commands/ShortcunCommands.h +++ b/Source/Cut5/Widgets/Commands/ShortcunCommands.h @@ -22,6 +22,10 @@ public: TSharedPtr TimelineMoveLeft; TSharedPtr TimelineMoveRight; - + TSharedPtr StartCollectGarbage; + TSharedPtr EndCollectGarbage; + + TSharedPtr Copy; + TSharedPtr Paste; }; diff --git a/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp b/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp index 7c14387..e441f71 100644 --- a/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp +++ b/Source/Cut5/Widgets/Commands/ShortcutCommands.cpp @@ -14,6 +14,12 @@ void FShortCutCommands::RegisterCommands() UI_COMMAND(RightPerFrame, "右移一帧", "Executes My FCurtainCommands", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::RightBracket)); UI_COMMAND(TimelineMoveLeft, "左侧移动时间轴", "Executes My FCurtainCommands", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::MouseScrollDown, EModifierKey::Shift)); UI_COMMAND(TimelineMoveRight, "右侧移动时间轴", "Executes My FCurtainCommands", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::MouseScrollUp, EModifierKey::Shift)); + + UI_COMMAND(StartCollectGarbage, "启用垃圾回收", "Executes Start Garbage Collect", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::O, EModifierKey::Shift)); + UI_COMMAND(EndCollectGarbage, "结束垃圾回收", "Executes End Garbage Collect", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::P, EModifierKey::Shift)); + + UI_COMMAND(Copy, "复制", "Executes Start Garbage Collect", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::C, EModifierKey::Control)); + UI_COMMAND(Paste, "粘贴", "Executes End Garbage Collect", EUserInterfaceActionType::ToggleButton, FInputChord(EKeys::V, EModifierKey::Control)); } #undef LOCTEXT_NAMESPACE diff --git a/Source/Cut5/Widgets/Curtain/SCurtainPanel.cpp b/Source/Cut5/Widgets/Curtain/SCurtainPanel.cpp index 74500a9..6e65830 100644 --- a/Source/Cut5/Widgets/Curtain/SCurtainPanel.cpp +++ b/Source/Cut5/Widgets/Curtain/SCurtainPanel.cpp @@ -36,13 +36,39 @@ void SCurtainPanel::Construct(const FArguments& InArgs) { RenameCurtain(); })); - CommandList->MapAction(FCurtainCommands::Get().MoveUp, FExecuteAction::CreateLambda([]() + CommandList->MapAction(FCurtainCommands::Get().MoveUp, FExecuteAction::CreateLambda([this]() { - + if (!IsCurtainGroup(SelectedGuid)) + { + FCurtainGroup* Group = nullptr; + FCurtain* Curtain = FindCurtain(SelectedGuid, Group); + if (Curtain - Group->Curtains.GetData() - 1 >= 0) + { + Move(Curtain - Group->Curtains.GetData(), Curtain - Group->Curtains.GetData() - 1, Group - Groups.GetData(), Group - Groups.GetData()); + } + + } + else + { + + } })); - CommandList->MapAction(FCurtainCommands::Get().MoveDown, FExecuteAction::CreateLambda([]() + CommandList->MapAction(FCurtainCommands::Get().MoveDown, FExecuteAction::CreateLambda([this]() { - + if (!IsCurtainGroup(SelectedGuid)) + { + FCurtainGroup* Group = nullptr; + FCurtain* Curtain = FindCurtain(SelectedGuid, Group); + if ((Curtain - Group->Curtains.GetData() + 1) < Group->Curtains.Num()) + { + Move(Curtain - Group->Curtains.GetData(), Curtain - Group->Curtains.GetData() + 2, Group - Groups.GetData(), Group - Groups.GetData()); + } + + } + else + { + + } })); CommandList->MapAction(FCurtainCommands::Get().InsertUp, FExecuteAction::CreateLambda([this]() { @@ -396,4 +422,44 @@ void SCurtainPanel::RenameCurtain() } } +FCurtain* SCurtainPanel::FindCurtain(const FGuid& Guid, FCurtainGroup*& OutGroup) +{ + for (FCurtainGroup& Group : Groups) + { + for (FCurtain& Curtain : Group.Curtains) + { + if (Curtain.CurtainUUID == Guid) + { + OutGroup = &Group; + return &Curtain; + } + } + } + return nullptr; +} + +FCurtainGroup* SCurtainPanel::FindCurtainGroup(const FGuid& Guid) +{ + for (FCurtainGroup& Group : Groups) + { + if (Group.GroupUID == Guid) + { + return &Group; + } + } + return nullptr; +} + +bool SCurtainPanel::IsCurtainGroup(const FGuid& Guid) +{ + for (FCurtainGroup& Group : Groups) + { + if (Group.GroupUID == Guid) + { + return true; + } + } + return false; +} + END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/Curtain/SCurtainPanel.h b/Source/Cut5/Widgets/Curtain/SCurtainPanel.h index 307951b..4f38812 100644 --- a/Source/Cut5/Widgets/Curtain/SCurtainPanel.h +++ b/Source/Cut5/Widgets/Curtain/SCurtainPanel.h @@ -40,6 +40,10 @@ public: void ShowCurtainCommand(const FGuid& Guid); void RemoveCurtain(); void RenameCurtain(); + FCurtain* FindCurtain(const FGuid& Guid, FCurtainGroup*& OutGroup); + FCurtainGroup* FindCurtainGroup(const FGuid& Guid); + bool IsCurtainGroup(const FGuid& Guid); + ICutMainWidgetInterface* MainWidgetInterface = nullptr; int32 CurrentSelectedTree = 0; TArray> Trees; diff --git a/Source/Cut5/Widgets/DefineGlobal.h b/Source/Cut5/Widgets/DefineGlobal.h index bc4cbd3..9bbe4f0 100644 --- a/Source/Cut5/Widgets/DefineGlobal.h +++ b/Source/Cut5/Widgets/DefineGlobal.h @@ -324,14 +324,17 @@ struct CUT5_API FClipData int32 GetClipRelativeEndFrame() const { return ClipEndFrame - ClipStartFrame; } FPresetsCustomData PresetsCustomData; + + TArray MovieBrushes; TArray MovieBrushesPath; int32 MovieBrushNum; + TArray AudioBrushes; TArray AudioBrushLength; - + int32 AudioBrushNum; enum class ECropMethod { diff --git a/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp b/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp index 1fff968..5700aae 100644 --- a/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp +++ b/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp @@ -438,8 +438,6 @@ void DragDropOperator::OnDragOver(const FGeometry& MyGeometry, const FDragDropEv if (DragDropOperation.DragType == FClip2ClipDragDropOperation::EDragType::Move) { TSharedPtr TimelineClip = StaticCastSharedPtr(DragDropOperation.DraggingWidget); - // TimelineClip->ClipData->ClipStartFrame += NewPos; - // TimelineClip->ClipData->ClipEndFrame += NewPos; if (TimelineClip->ClipData->ClipStartFrame + NewPos >= 0) { for (FSingleTrackGroupInstance& TrackGroupInstance : Body->MainWidgetInterface->GetCutTimeline()->TrackGroupInstances) @@ -456,6 +454,7 @@ void DragDropOperator::OnDragOver(const FGeometry& MyGeometry, const FDragDropEv FVector2D(((TimelineClip->ClipData->ClipEndFrame + NewPos) - (TimelineClip->ClipData->ClipStartFrame + NewPos)) * FGlobalData::DefaultTimeTickSpace, Body->GetCachedGeometry().GetLocalSize().Y), FUtils::DetectDragTypeCanDrop(*TimelineClip->ClipData, Body->TrackHead->TrackData.TrackType) ? FLinearColor(0, 1, 0, 1) : FLinearColor(1, 0, 0, 1)); Body->DragDropShowProperties.Add(NewProperty); + Body->MainWidgetInterface->GetCutTimeline()->CurrentTimeData->SetText(FText::FromString(FGlobalData::GetTimeData(TimelineClip->ClipData->ClipStartFrame + NewPos) + " / ")); } @@ -637,7 +636,7 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& NewClipData.ClipGuid = FGuid::NewGuid(); NewClipData.ClipType = TrackHead->TrackData.TrackType; NewClipData.ClipStartFrame = MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition()).X / FGlobalData::DefaultTimeTickSpace; - NewClipData.ClipEndFrame = (MyGeometry.AbsoluteToLocal(DragDropEvent.GetScreenSpacePosition()).X + 100) / FGlobalData::DefaultTimeTickSpace; + NewClipData.ClipEndFrame = NewClipData.ClipStartFrame + 20; if (PresetDragOperation->PresetData.PresetType == EPresetType::Color) { if (PresetDragOperation->PresetData.Colors.Num() == 0) @@ -778,7 +777,7 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& if (TrackHead->TrackData.TrackType == ETrackType::LightArrayTrack) { NewClipData.ClipType = ETrackType::LightArrayTrack; - NewClipData.LightArrayData = FOpencvUtils::GetVideoFrameLightArray(ClipDragOperation.TimelinePropertyData->MoviePath, FGlobalData::LightArrayX, FGlobalData::LightArrayY); + NewClipData.LightArrayData = FFFMPEGUtils::GetVideoFrameLightArray(ClipDragOperation.TimelinePropertyData->MoviePath, FGlobalData::LightArrayX, FGlobalData::LightArrayY); } if (TrackHead->TrackData.TrackType == ETrackType::PlayerTrack) { @@ -856,7 +855,6 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& const int32 Offset = TimelineClip->ClipData->ClipEndFrame - TimelineClip->ClipData->ClipStartFrame; TimelineClip->ClipData->ClipStartFrame = 0; TimelineClip->ClipData->ClipEndFrame = Offset; - } else { @@ -875,12 +873,12 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& // It mean the clip is not in the same track FClipData NewClipData = *TimelineClip->ClipData; - NewClipData.ClipType = TrackHead->TrackData.TrackType; + if (FUtils::DetectDragTypeCanDrop(NewClipData, TrackHead->TrackData.TrackType) == false) { return; } - + NewClipData.ClipType = TrackHead->TrackData.TrackType; TSharedPtr OriginTrackBody = StaticCastSharedPtr(TimelineClip->Body); OriginTrackBody->TrackHead->TrackData.ClipData.Remove(NewClipData); TrackHead->TrackData.ClipData.Add(NewClipData); diff --git a/Source/Cut5/Widgets/SCutMainWindow.cpp b/Source/Cut5/Widgets/SCutMainWindow.cpp index 7099684..db5fd15 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.cpp +++ b/Source/Cut5/Widgets/SCutMainWindow.cpp @@ -416,17 +416,25 @@ void SCutMainWindow::Construct(const FArguments& InArgs) })); CommandList->MapAction(FShortCutCommands::Get().ZoomInTimeline, FExecuteAction::CreateLambda([this]() { - const float NewValue = CutTimeline->ZoomSlider->GetValue() + 0.1; - CutTimeline->ZoomSlider->SetValue(NewValue); - FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), NewValue); - CutTimeline->RenderGroup(); + if (CutTimeline->ZoomSlider->GetValue() <= 1.0) + { + const float NewValue = CutTimeline->ZoomSlider->GetValue() + 0.1; + CutTimeline->ZoomSlider->SetValue(NewValue); + FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), NewValue); + CutTimeline->RenderGroup(); + CutTimeline->UpdateCursorPosition(CutTimeline->GetCursorPosition()); + } })); CommandList->MapAction(FShortCutCommands::Get().ZoomOutTimeline, FExecuteAction::CreateLambda([this]() { - const float NewValue = CutTimeline->ZoomSlider->GetValue() - 0.1; - CutTimeline->ZoomSlider->SetValue(NewValue); - FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), NewValue); - CutTimeline->RenderGroup(); + if (CutTimeline->ZoomSlider->GetValue() >= 0.1) + { + const float NewValue = CutTimeline->ZoomSlider->GetValue() - 0.1; + CutTimeline->ZoomSlider->SetValue(NewValue); + FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), NewValue); + CutTimeline->RenderGroup(); + CutTimeline->UpdateCursorPosition(CutTimeline->GetCursorPosition()); + } })); CommandList->MapAction(FShortCutCommands::Get().SelectMode, FExecuteAction::CreateLambda([this]() @@ -448,11 +456,63 @@ void SCutMainWindow::Construct(const FArguments& InArgs) ]; GEngine->GameViewport->AddSoftwareCursorFromSlateWidget(EMouseCursor::Type::Default, NewWidget.ToSharedRef()); })); + CommandList->MapAction(FShortCutCommands::Get().TimelineMoveLeft, FExecuteAction::CreateLambda([this]() + { + if (CutTimeline->TrackBodyHScrollBox->GetScrollOffset() < CutTimeline->TrackBodyHScrollBox->GetScrollOffsetOfEnd()) + CutTimeline->TrackBodyHScrollBox->SetScrollOffset(CutTimeline->TrackBodyHScrollBox->GetScrollOffset() - 10); + })); + CommandList->MapAction(FShortCutCommands::Get().TimelineMoveRight, FExecuteAction::CreateLambda([this]() + { + if (CutTimeline->TrackBodyHScrollBox->GetScrollOffset() < CutTimeline->TrackBodyHScrollBox->GetScrollOffsetOfEnd()) + CutTimeline->TrackBodyHScrollBox->SetScrollOffset(CutTimeline->TrackBodyHScrollBox->GetScrollOffset() + 10); + })); + + CommandList->MapAction(FShortCutCommands::Get().StartCollectGarbage, FExecuteAction::CreateLambda([this]() + { + GEngine->SetTimeUntilNextGarbageCollection(1); + GEngine->ForceGarbageCollection(true); + })); - // FRunnableThread* Thread = FRunnableThread::Create(SoundThread, TEXT("SoundThread")); - // OpenProject(FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("DefaultProject"))); - // ImportProject(""); - + CommandList->MapAction(FShortCutCommands::Get().Copy, FExecuteAction::CreateLambda([this]() + { + CutTimeline->CopyClipData.Empty(); + for (FSingleTrackGroupInstance& Instance : CutTimeline->TrackGroupInstances) + { + TSharedPtr TrackBody = StaticCastSharedPtr(Instance.Body); + for (FClipData& ClipData : TrackBody->TrackHead->TrackData.ClipData) + { + if (CutTimeline->SelectedClips.Contains(ClipData.ClipGuid)) + { + CutTimeline->CopyClipData.Add(ClipData); + break; + } + + } + } + })); + CommandList->MapAction(FShortCutCommands::Get().Paste, FExecuteAction::CreateLambda([this]() + { + + for (FClipData& ClipData : CutTimeline->CopyClipData) + { + for (FSingleTrackGroupInstance& Instance : CutTimeline->TrackGroupInstances) + { + TSharedPtr TrackBody = StaticCastSharedPtr(Instance.Body); + FVector2D Pos = TrackBody->GetCachedGeometry().AbsoluteToLocal(NewMouseEvent.GetScreenSpacePosition()); + if (TrackBody->TrackHead->TrackData.DeviceTrack.Guid == ClipData.BindTrackGuid) + { + FClipData NewClipData = ClipData; + NewClipData.ClipGuid = FGuid::NewGuid(); + int32 Length = NewClipData.ClipEndFrame - NewClipData.ClipStartFrame; + NewClipData.ClipStartFrame = FMath::RoundToInt(Pos.X / FGlobalData::DefaultTimeTickSpace); + NewClipData.ClipEndFrame = NewClipData.ClipStartFrame + Length; + DragDropOperator::GetDragDropOperator()->UpdateClipProcess(this, NewClipData); + TrackBody->TrackHead->TrackData.ClipData.Add(NewClipData); + TrackBody->CallRender(); + } + } + } + })); FMainMenuCommands::Register(); CommandList->MapAction(FMainMenuCommands::Get().ExportXML, FExecuteAction::CreateLambda([this]() @@ -483,22 +543,21 @@ void SCutMainWindow::Construct(const FArguments& InArgs) SaveProject(); })); - CommandList->MapAction(FShortCutCommands::Get().TimelineMoveLeft, FExecuteAction::CreateLambda([this]() - { - if (CutTimeline->TrackBodyHScrollBox->GetScrollOffset() < CutTimeline->TrackBodyHScrollBox->GetScrollOffsetOfEnd()) - CutTimeline->TrackBodyHScrollBox->SetScrollOffset(CutTimeline->TrackBodyHScrollBox->GetScrollOffset() - 10); - })); - CommandList->MapAction(FShortCutCommands::Get().TimelineMoveRight, FExecuteAction::CreateLambda([this]() - { - if (CutTimeline->TrackBodyHScrollBox->GetScrollOffset() < CutTimeline->TrackBodyHScrollBox->GetScrollOffsetOfEnd()) - CutTimeline->TrackBodyHScrollBox->SetScrollOffset(CutTimeline->TrackBodyHScrollBox->GetScrollOffset() + 10); - })); + + FInputBindingManager::Get().RegisterCommandList(FShortCutCommands::Get().GetContextName(), CommandList.ToSharedRef()); OnUpdateProjector(0, true); DragDropOperator::GetDragDropOperator()->SavedMainInterface = this; } + +FReply SCutMainWindow::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + this->NewMouseEvent = MouseEvent; + return SCompoundWidget::OnMouseMove(MyGeometry, MouseEvent); +} + void SCutMainWindow::Render() { StatePanel->PlayerList1->ClearChildren(); @@ -560,7 +619,7 @@ int32 SCutMainWindow::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedG FSlateDrawElement::MakeBox( OutDrawElements, - 999999, + LayerId + 4, AllottedGeometry.ToPaintGeometry(FVector2f(2, CutTimeline->GetCachedGeometry().Size.Y), FSlateLayoutTransform(FVector2f(RenderLineTime, AllottedGeometry.GetLocalSize().Y - CutTimeline->GetCachedGeometry().GetLocalSize().Y))), &Brush, ESlateDrawEffect::None, @@ -572,8 +631,7 @@ int32 SCutMainWindow::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedG - return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, - bParentEnabled); + return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); } FReply SCutMainWindow::OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) diff --git a/Source/Cut5/Widgets/SCutMainWindow.h b/Source/Cut5/Widgets/SCutMainWindow.h index 4b50215..7832cd5 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.h +++ b/Source/Cut5/Widgets/SCutMainWindow.h @@ -88,11 +88,13 @@ public: virtual void OpenColorPanel(FLinearColor* ColorPtr); virtual void AddNewCustomPreset(const FString& Name, const FPresetsCustomData CustomData) override; virtual ESelectMode GetSelectedMode() override; - + + FPointerEvent NewMouseEvent; virtual bool SupportsKeyboardFocus() const override { return true; }; virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; tinyxml2::XMLElement* GetDeviceElement(tinyxml2::XMLElement* Parent); tinyxml2::XMLElement* GetVideoElement(tinyxml2::XMLElement* Parent, FEncodeVideoInfo EncodeVideoInfo); @@ -106,3 +108,5 @@ public: tinyxml2::XMLElement* GetSpecialEffectGroup(tinyxml2::XMLElement* Parent, FEffectCardGroup* Group); tinyxml2::XMLElement* GetSpecialEffect(tinyxml2::XMLElement* Parent, FEffectCardProperty* Effect); }; + + diff --git a/Source/Cut5/Widgets/SCutTimeline.cpp b/Source/Cut5/Widgets/SCutTimeline.cpp index 22c3679..7eaebdf 100644 --- a/Source/Cut5/Widgets/SCutTimeline.cpp +++ b/Source/Cut5/Widgets/SCutTimeline.cpp @@ -270,6 +270,7 @@ void SCutTimeline::Construct(const FArguments& InArgs) [ SAssignNew(TickScrollBox, SScrollBox) .ScrollBarVisibility(EVisibility::Hidden) + .WheelScrollMultiplier(0.0) .Orientation(EOrientation::Orient_Horizontal) + SScrollBox::Slot() [ @@ -711,13 +712,13 @@ bool SCutTimeline::LoadTimeline(const FString& LoadPath, FTimelineInfo& Info) return true; } -void SCutTimeline::RemoveTrack(const FGuid& TrackGuid) +void SCutTimeline::RemoveTrack(const FGuid& DeviceGuid) { - for (int32 i = 0; i < TrackGroups.Num(); i++) + for (int32 i = 0; i < DeviceTrackGroups.Num(); i++) { - for (int32 j = DeviceTrackGroups.Num() - 1; j >= 0; --j) + for (int32 j = DeviceTrackGroups[i].DeviceTracks.Num() - 1; j >= 0; --j) { - if (DeviceTrackGroups[i].DeviceTracks[j].Guid == TrackGuid) + if (DeviceTrackGroups[i].DeviceTracks[j].Guid == DeviceGuid) { DeviceTrackGroups[i].DeviceTracks.RemoveAt(j); break; @@ -726,13 +727,13 @@ void SCutTimeline::RemoveTrack(const FGuid& TrackGuid) } for (int32 i = TrackGroupInstances.Num() - 1; i >= 0; --i) { - if (StaticCastSharedPtr(TrackGroupInstances[i].Head)->TrackData.Guid == TrackGuid) + if (StaticCastSharedPtr(TrackGroupInstances[i].Head)->TrackData.DeviceTrack.Guid == DeviceGuid) { TrackGroupInstances.RemoveAt(i); } } RenderGroup(); - MainWidgetInterface->OnRemoveTrack(TrackGuid); + MainWidgetInterface->OnRemoveTrack(DeviceGuid); } FTrackGroup* SCutTimeline::GetTrackGroupByName(FString GroupName) diff --git a/Source/Cut5/Widgets/SCutTimeline.h b/Source/Cut5/Widgets/SCutTimeline.h index 582f82e..31d185d 100644 --- a/Source/Cut5/Widgets/SCutTimeline.h +++ b/Source/Cut5/Widgets/SCutTimeline.h @@ -90,7 +90,7 @@ public: * @brief Remove Track from Timeline by UUID. * @param FGuid Track UUID. */ - void RemoveTrack(const FGuid& FGuid); + void RemoveTrack(const FGuid& DeviceGuid); FTrackGroup* GetTrackGroupByName(FString GroupName); @@ -118,6 +118,7 @@ public: * @brief Selected Clips Guid, Use for Multi-Select. */ TArray SelectedClips; + TArray CopyClipData; diff --git a/Source/Cut5/Widgets/STimelineClip.cpp b/Source/Cut5/Widgets/STimelineClip.cpp index b5557d3..64c4057 100644 --- a/Source/Cut5/Widgets/STimelineClip.cpp +++ b/Source/Cut5/Widgets/STimelineClip.cpp @@ -7,12 +7,14 @@ #include "AudioDevice.h" +#include "ImageUtils.h" #include "SCutTimeline.h" #include "SlateOptMacros.h" #include "STrackBody.h" #include "Commands/TimelineClipCommands.h" #include "Cut5/WidgetInterface.h" #include "Cut5/Interface/SoundInterface.h" +#include "Cut5/Utils/FFMPEGUtils.h" #include "Cut5/Utils/Utils.h" #include "DragDropOperator/DragDropOperator.h" #include "Engine/Engine.h" @@ -56,7 +58,7 @@ FReply STimelineClip::OnBorderMouseButtonDown(const FGeometry& Geometry, const F } - const FVector2D LocalPos = Geometry.AbsoluteToLocal(PointerEvent.GetScreenSpacePosition()); + LocalPos = Geometry.AbsoluteToLocal(PointerEvent.GetScreenSpacePosition()); const float DragOffset = MainWidgetInterface->GetCutTimeline()->GetCachedGeometry().AbsoluteToLocal(PointerEvent.GetScreenSpacePosition()).X; if (LocalPos.X <= 10) { @@ -434,55 +436,55 @@ void STimelineClip::Seek(int32 Frame) } - break; + if (ClipData->PresetType == EPresetType::Color) + { + + TArray Colors; + Colors.Init(ClipData->ClipColors[0].ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); + MainWidgetInterface->OnUpdateLightArray(Colors); + break; + } + else if (ClipData->PresetType == EPresetType::Gradient) + { + int32 Between = -1; + for (int32 i = 0; i < ClipData->Cursors.Num() - 1; i++) + { + if (SeekMovieFrame >= ClipData->Cursors[i].CursorFrameOffset && SeekMovieFrame <= ClipData->Cursors[i + 1].CursorFrameOffset) + { + Between = i; + } + } + if (SeekMovieFrame >= ClipData->Cursors[ClipData->Cursors.Num() - 1].CursorFrameOffset) + { + Between = ClipData->Cursors.Num() - 1; + } + if (Between != -1) + { + if (Between == ClipData->Cursors.Num() - 1) + { + TArray Colors; + Colors.Init(ClipData->Cursors[ClipData->Cursors.Num() - 1].Color.ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); + MainWidgetInterface->OnUpdateLightArray(Colors); + } + else + { + TArray Colors; + Colors.Init(FLinearColor::Black.ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); + for (int32 i = 0; i < FGlobalData::LightArrayX * FGlobalData::LightArrayY; i++) + { + Colors[i] = FMath::Lerp(ClipData->Cursors[Between].Color, ClipData->Cursors[Between + 1].Color, (float)(SeekMovieFrame - ClipData->Cursors[Between].CursorFrameOffset) / (float)(ClipData->Cursors[Between + 1].CursorFrameOffset - ClipData->Cursors[Between].CursorFrameOffset)).ToFColor(false); + } + MainWidgetInterface->OnUpdateLightArray(Colors); + } + + } + + break; + } } - if (ClipData->PresetType == EPresetType::Color) - { - - TArray Colors; - Colors.Init(ClipData->ClipColors[0].ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); - MainWidgetInterface->OnUpdateLightArray(Colors); - break; - } - else if (ClipData->PresetType == EPresetType::Gradient) - { - int32 Between = -1; - for (int32 i = 0; i < ClipData->Cursors.Num() - 1; i++) - { - if (SeekMovieFrame >= ClipData->Cursors[i].CursorFrameOffset && SeekMovieFrame <= ClipData->Cursors[i + 1].CursorFrameOffset) - { - Between = i; - } - } - if (SeekMovieFrame >= ClipData->Cursors[ClipData->Cursors.Num() - 1].CursorFrameOffset) - { - Between = ClipData->Cursors.Num() - 1; - } - if (Between != -1) - { - if (Between == ClipData->Cursors.Num() - 1) - { - TArray Colors; - Colors.Init(ClipData->Cursors[ClipData->Cursors.Num() - 1].Color.ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); - MainWidgetInterface->OnUpdateLightArray(Colors); - } - else - { - TArray Colors; - Colors.Init(FLinearColor::Black.ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); - for (int32 i = 0; i < FGlobalData::LightArrayX * FGlobalData::LightArrayY; i++) - { - Colors[i] = FMath::Lerp(ClipData->Cursors[Between].Color, ClipData->Cursors[Between + 1].Color, (float)(SeekMovieFrame - ClipData->Cursors[Between].CursorFrameOffset) / (float)(ClipData->Cursors[Between + 1].CursorFrameOffset - ClipData->Cursors[Between].CursorFrameOffset)).ToFColor(false); - } - MainWidgetInterface->OnUpdateLightArray(Colors); - } - - } - - break; - } + if (SeekMovieFrame < ClipData->LightArrayData.Num()) @@ -676,7 +678,7 @@ void STimelineClip::UpdateMove(int32 X, int32 DragOffset) FReply STimelineClip::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - const FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); + LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); bNeedPaintDrag = false; if (LocalPos.X <= 10) { @@ -688,6 +690,12 @@ FReply STimelineClip::OnMouseMove(const FGeometry& MyGeometry, const FPointerEve bNeedPaintDrag = true; PaintDragType = 1; } + + if (MainWidgetInterface->GetSelectedMode() == ESelectMode::CutMode) + { + bNeedPaintDrag = true; + PaintDragType = 2; + } return FReply::Handled(); } @@ -706,22 +714,22 @@ int32 STimelineClip::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe const int32 PicLength = TotalLength / ClipData->AudioBrushes.Num(); for (FSlateBrush& SlateBrush : ClipData->AudioBrushes) { - FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 3, AllottedGeometry.ToPaintGeometry(FVector2f(PicLength, AllottedGeometry.GetLocalSize().Y), FSlateLayoutTransform(FVector2f(i * PicLength, 0))), &SlateBrush); + FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 4, AllottedGeometry.ToPaintGeometry(FVector2f(PicLength, AllottedGeometry.GetLocalSize().Y), FSlateLayoutTransform(FVector2f(i * PicLength, 0))), &SlateBrush); i++; } } } - if (ClipData->MovieBrushes.Num() > 0) + if (ClipData->MovieBrushNum > 0) { - int32 Step = ClipData->MovieBrushes.Num(); - const int32 PerImageLength = TotalLength / ClipData->MovieBrushesPath.Num(); + int32 Step = ClipData->MovieBrushNum; + const int32 PerImageLength = TotalLength / ClipData->MovieBrushNum; const int32 ShouldPerImageLength = TotalLength / 128; if (ShouldPerImageLength != 0) { - Step = ClipData->MovieBrushes.Num() / ShouldPerImageLength; + Step = ClipData->MovieBrushNum / ShouldPerImageLength; } @@ -729,14 +737,14 @@ int32 STimelineClip::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe int32 Current = 0; for (int32 j = 0; j < ShouldPerImageLength; j++) { - if (ClipData->MovieBrushes.Num() > 0) + if (ClipData->MovieBrushes.Num() > 0 && Current < ClipData->MovieBrushes.Num()) NewBrushes.Add(ClipData->MovieBrushes[Current]); Current += Step; } int32 i = 0; if (NewBrushes.Num() == 0) { - NewBrushes.Add(ClipData->MovieBrushes[0]); + NewBrushes.Add(ClipData->MovieBrushes.Num() > 0 ? ClipData->MovieBrushes[0] : FSlateBrush()); } for (FSlateBrush& SlateBrush : NewBrushes) { @@ -765,9 +773,8 @@ int32 STimelineClip::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe Vectors.Add(FVector2f(0, AllottedGeometry.Size.Y)); Vectors.Add(FVector2f(AllottedGeometry.Size.X, AllottedGeometry.Size.Y)); Vectors.Add(FVector2f(AllottedGeometry.Size.X, 0)); - FSlateDrawElement::MakeLines(OutDrawElements, LayerId + 3, AllottedGeometry.ToPaintGeometry(), Vectors, ESlateDrawEffect::None, FLinearColor::White, true, 2.0f); - return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId + 1, InWidgetStyle, - bParentEnabled); + Vectors.Add(FVector2f(0, 0)); + FSlateDrawElement::MakeLines(OutDrawElements, LayerId + 4, AllottedGeometry.ToPaintGeometry(), Vectors, ESlateDrawEffect::None, FLinearColor::White, true, 2.0f); } @@ -832,6 +839,12 @@ int32 STimelineClip::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 3, AllottedGeometry.ToPaintGeometry(FVector2f(10, AllottedGeometry.Size.Y), FSlateLayoutTransform(FVector2f(AllottedGeometry.Size.X - 10, 0))), &Brush, ESlateDrawEffect::None, FLinearColor(1.0, 0.0, 1.0, 1.0)); } + else if (PaintDragType == 2) + { + const FSlateBrush Brush; + FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 3, AllottedGeometry.ToPaintGeometry(FVector2f(FGlobalData::DefaultTimeTickSpace, FGlobalData::DefaultTrackHeight), FSlateLayoutTransform(FVector2f(LocalPos.X, 0))), + &Brush, ESlateDrawEffect::None, FLinearColor(1.0, 0.0, 1.0, 1.0)); + } } @@ -852,6 +865,8 @@ void STimelineClip::DoSound(ESoundSolveType SolveType, int32 InFrame) { if (SoundThread == nullptr) { + if (ClipData->ResourcePropertyDataPtr == nullptr) + return; if (ClipData->ResourcePropertyDataPtr->AudioStream != -1) { SoundThread = new FSoundThread(2, ClipData->ResourcePropertyDataPtr->AudioSample); @@ -890,5 +905,82 @@ void STimelineClip::OnMouseLeave(const FPointerEvent& MouseEvent) bNeedPaintDrag = false; } +void STimelineClip::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) +{ + if (ClipData->MovieBrushes.Num() < ClipData->MovieBrushNum && ClipData->ResourcePropertyDataPtr && ClipData->ResourcePropertyDataPtr->Context && ClipData->ResourcePropertyDataPtr->VideoStream != -1) + { + if (ThumbnailCodecContext.Context == nullptr) + { + FFFMPEGUtils::LoadContextPure(ClipData->ResourcePropertyDataPtr->MoviePath, &ThumbnailCodecContext); + return; + } + + const int32 CurrentFrame = ClipData->MovieBrushNum - (ClipData->MovieBrushNum - ClipData->MovieBrushes.Num()); + const int64 Timestamp = av_rescale_q(CurrentFrame / 30.0 * AV_TIME_BASE, AVRational{1, AV_TIME_BASE}, ThumbnailCodecContext.Context->streams[ThumbnailCodecContext.VideoStream]->time_base); + av_seek_frame(ThumbnailCodecContext.Context, ThumbnailCodecContext.VideoStream, Timestamp, AVSEEK_FLAG_BACKWARD); + AVPacket Packet; + av_init_packet(&Packet); + AVFormatContext* FormatContext = ThumbnailCodecContext.Context; + AVCodecContext* VideoCodecContext = ThumbnailCodecContext.VideoCodecContext; + AVCodec* VideoCodec = ThumbnailCodecContext.VideoCodec; + AVFrame* Frame = av_frame_alloc(); + + while (av_read_frame(FormatContext, &Packet) >= 0) + { + if (avcodec_send_packet(VideoCodecContext, &Packet) < 0) + { + + } + if (avcodec_receive_frame(VideoCodecContext, Frame) >= 0) + { + break; + } + }; + + + + + if (Frame) + { + struct SwsContext* swsCtx = sws_getContext( + Frame->width, Frame->height, VideoCodecContext->pix_fmt, + Frame->width / 10, Frame->height / 10, AV_PIX_FMT_BGRA, + SWS_BILINEAR, NULL, NULL, NULL + ); + if (!swsCtx) + { + UE_LOG(LogTemp, Error, TEXT("Error creating swsContext")); + } + uint8* RawData = new uint8[(Frame->width / 10) * (Frame->height / 10) * 4]; + uint8* dest[4] = {RawData, 0, 0, 0}; + int32 dest_linesize[4] = {(Frame->width / 10) * 4, 0, 0, 0}; + sws_scale(swsCtx, Frame->data, Frame->linesize, 0, Frame->height, dest, dest_linesize); + sws_freeContext(swsCtx); + + + UTexture2D* Texture = UTexture2D::CreateTransient(Frame->width / 10, Frame->height / 10, PF_B8G8R8A8); + if (Texture) + { + void* MipData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); + FMemory::Memcpy(MipData, RawData, (Frame->width / 10) * (Frame->height / 10) * 4); + Texture->GetPlatformData()->Mips[0].BulkData.Unlock(); + Texture->UpdateResource(); + + FGuid Guid = FGuid::NewGuid(); + FFFMPEGUtils::ExportImage(Texture, *FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png")); + ClipData->MovieBrushesPath.Add(FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png")); + FSlateDynamicImageBrush Brush = FSlateDynamicImageBrush(*ClipData->MovieBrushesPath[ClipData->MovieBrushesPath.Num() - 1], FVector2f(0, 0)); + ClipData->MovieBrushes.Add(Brush); + + Texture->MarkAsGarbage(); + delete RawData; + } + + + } + + } +} + END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/STimelineClip.h b/Source/Cut5/Widgets/STimelineClip.h index 0a20dc7..2f0fcd3 100644 --- a/Source/Cut5/Widgets/STimelineClip.h +++ b/Source/Cut5/Widgets/STimelineClip.h @@ -68,8 +68,12 @@ public: bool bNeedPaintDrag = false; int32 PaintDragType = 0; - + FVector2D LocalPos; bool bIsDragOver = false; - + + virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; + + + FTimelinePropertyData ThumbnailCodecContext; }; diff --git a/Source/Cut5/Widgets/STimelineTick.cpp b/Source/Cut5/Widgets/STimelineTick.cpp index fa57bd7..ef2069f 100644 --- a/Source/Cut5/Widgets/STimelineTick.cpp +++ b/Source/Cut5/Widgets/STimelineTick.cpp @@ -67,7 +67,11 @@ int32 STimelineTick::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe ESlateDrawEffect::None, FColor(55, 55, 55, 255)); } - if (j % 30 == 0) + + const float Space = FGlobalData::GlobalFPS * FGlobalData::DefaultTimeTickSpace; + const int32 Multiplier = FMath::GetMappedRangeValueClamped(FVector2D(300, 2), FVector2D(1, 5), Space); + + if (j % (Multiplier * 30) == 0) { const FSlateBrush Brush; FSlateDrawElement::MakeBox( diff --git a/Source/Cut5/Widgets/STrackHead.cpp b/Source/Cut5/Widgets/STrackHead.cpp index 90a7d15..59ef25a 100644 --- a/Source/Cut5/Widgets/STrackHead.cpp +++ b/Source/Cut5/Widgets/STrackHead.cpp @@ -25,11 +25,11 @@ void STrackHead::Construct(const FArguments& InArgs) { if (TrackData.DeviceTrack.DeviceTrackGroup->GroupName != TEXT("固定轨道")) { - CutTimeline->RemoveTrack(TrackData.Guid); + CutTimeline->RemoveTrack(TrackData.DeviceTrack.Guid); } })); - + })); TSharedPtr Image = SNew(SImage); ChildSlot diff --git a/Source/Cut5/Widgets/StatePanel/SVideoPlayer.cpp b/Source/Cut5/Widgets/StatePanel/SVideoPlayer.cpp index 62e1fbd..71294b8 100644 --- a/Source/Cut5/Widgets/StatePanel/SVideoPlayer.cpp +++ b/Source/Cut5/Widgets/StatePanel/SVideoPlayer.cpp @@ -8,6 +8,14 @@ BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION +SVideoPlayer::~SVideoPlayer() +{ + if (Texture->IsValidLowLevel() && Texture->IsRooted()) + { + Texture->RemoveFromRoot(); + } +} + void SVideoPlayer::Construct(const FArguments& InArgs) { @@ -33,12 +41,13 @@ void SVideoPlayer::UpdateVideoData(FGuid UUID, int32 X, int32 Y, uint8* RawData) VideoImage->SetImage(Brush); return; } - if (!Texture || Texture->GetSizeX() != X || Texture->GetSizeY() != Y) + if (!Texture->IsValidLowLevel() || Texture->GetSizeX() != X || Texture->GetSizeY() != Y) { Texture = UTexture2D::CreateTransient(X, Y, PF_B8G8R8A8); + Texture->AddToRoot(); } - if (Texture) + if (Texture->IsValidLowLevel()) { void* MipData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE); FMemory::Memcpy(MipData, RawData, X * Y * 4); diff --git a/Source/Cut5/Widgets/StatePanel/SVideoPlayer.h b/Source/Cut5/Widgets/StatePanel/SVideoPlayer.h index 3dbfeef..205d818 100644 --- a/Source/Cut5/Widgets/StatePanel/SVideoPlayer.h +++ b/Source/Cut5/Widgets/StatePanel/SVideoPlayer.h @@ -15,11 +15,13 @@ public: SLATE_BEGIN_ARGS(SVideoPlayer) { } - + SLATE_END_ARGS() + virtual ~SVideoPlayer() override; /** Constructs this widget with InArgs */ void Construct(const FArguments& InArgs); + void UpdateVideoData(FGuid UUID, int32 X, int32 Y, uint8* RawData); TObjectPtr Texture; TSharedPtr VideoImage;