diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index d325281..7728b9d 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -1,3 +1,10 @@ [/Script/EngineSettings.GeneralProjectSettings] ProjectID=DC8863584B4EB976B538D48259B614C0 +ProjectDisplayedTitle=NSLOCTEXT("[/Script/EngineSettings]", "AAF6DC1F478F1461B74DFBA59A7881EF", "编辑器") +ProjectDebugTitleInfo=NSLOCTEXT("[/Script/EngineSettings]", "BF92E12B419D88450FDB208106B9DE89", "\"") +bUseBorderlessWindow=True +bAllowMinimize=False +bAllowMaximize=False +bAllowClose=False + diff --git a/Resources/EngineLogo.ico b/Resources/EngineLogo.ico new file mode 100644 index 0000000..8b7ea06 Binary files /dev/null and b/Resources/EngineLogo.ico differ diff --git a/Source/Cut5/Interface/CutMainWidgetInterface.h b/Source/Cut5/Interface/CutMainWidgetInterface.h index db4b002..d228499 100644 --- a/Source/Cut5/Interface/CutMainWidgetInterface.h +++ b/Source/Cut5/Interface/CutMainWidgetInterface.h @@ -62,4 +62,5 @@ public: virtual void DeleteAllAssetsInTimeline() {}; virtual ESelectMode GetSelectedMode() { return ESelectMode::SelectMode; }; virtual class SCutTimeline* GetCutTimeline() { return nullptr; }; + virtual class SCutMainWindow* GetSelf() { return nullptr; }; }; diff --git a/Source/Cut5/Interface/SoundInterface.cpp b/Source/Cut5/Interface/SoundInterface.cpp index a442645..3f01287 100644 --- a/Source/Cut5/Interface/SoundInterface.cpp +++ b/Source/Cut5/Interface/SoundInterface.cpp @@ -3,17 +3,19 @@ #include #include "Cut5/Widgets/DefineGlobal.h" - +#include "Cut5/Utils/Utils.h" FCriticalSection Mutex; -FSoundThread::FSoundThread(int32 OutputChannel, int32 SampleRate) : FRunnable() +FSoundThread::FSoundThread(int32 OutputChannel, int32 SampleRate, int32 ByteNum, PaSampleFormat PaSoundType) : FRunnable() { Stream = nullptr; Pa_Initialize(); - Pa_OpenDefaultStream(&Stream, 0, OutputChannel, paFloat32, SampleRate, 0, nullptr, nullptr); + Pa_OpenDefaultStream(&Stream, 0, OutputChannel, PaSoundType, SampleRate, 10000, nullptr, nullptr); Pa_StartStream(Stream); this->OutputChannel = OutputChannel; this->SampleRate = SampleRate; + this->ByteNum = ByteNum; + this->PaSoundType = PaSoundType; } bool FSoundThread::Init() @@ -37,20 +39,23 @@ uint32 FSoundThread::Run() { while (!bStoped) { + if (SeekedFrame != 0) { - TArray> ResultData; - const int32 FrameLength = Audio.Num(); - for (int32 i = SeekedFrame * (SampleRate / 26) * 4 * 2; i < (SeekedFrame + 1) * (SampleRate / 26) * 4 * 2 && i < FrameLength; ++i) + const int32 Offset = SeekedFrame * (SampleRate / FGlobalData::GlobalFPS) * ByteNum * 2; + const int32 Size = SampleRate / FGlobalData::GlobalFPS; + if (Audio.Num() < Offset + Size) { - if (ResultData.Num() < i && i > 0) - { - ResultData.Add(Audio[i]); - } + SeekedFrame = 0; + continue; } - Pa_WriteStream(Stream, ResultData.GetData(), ResultData.Num() / 4 / 2); + if (Pa_WriteStream(Stream, Audio.GetData() + Offset, Size) == paOutputUnderflowed) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Underflowed")); + }; SeekedFrame = 0; } + } return 0; } @@ -61,6 +66,23 @@ void FSoundThread::Stop() FRunnable::Stop(); } +int FSoundThread::PaStreamCallback(const void* input, void* output, unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) +{ + // FSoundThread* This = static_cast(userData); + // + // TArray> ResultData; + // const int32 FrameLength = This->Audio.Num(); + // for (int32 i = This->SeekedFrame * (This->SampleRate / FGlobalData::GlobalFPS) * This->ByteNum * 2; i < This->SeekedFrame * (This->SampleRate / FGlobalData::GlobalFPS) * This->ByteNum * 2 + frameCount && i < FrameLength; ++i) + // { + // if (ResultData.Num() < i && i > 0) + // { + // ResultData.Add(This->Audio[i]); + // } + // } + // output = ResultData.GetData(); + return paContinue; +} bool FSoundThread::CopyAudio(const uint8* Data, int32 Size, ESoundSolveType SolveType) @@ -74,28 +96,28 @@ bool FSoundThread::CopyAudio(const uint8* Data, int32 Size, ESoundSolveType Solv } case ESoundSolveType::OnlyLeft: { - for (int32 i = 0; i < Size / 4; i++) + for (int32 i = 0; i < Size / ByteNum; i++) { if (i % 2 == 1) { - Audio[i * 4] = 0; - Audio[i * 4 + 1] = 0; - Audio[i * 4 + 2] = 0; - Audio[i * 4 + 3] = 0; + for (int32 j = 0; j < ByteNum; j++) + { + Audio[i * ByteNum + j] = 0; + } } } break; } case ESoundSolveType::OnlyRight: { - for (int32 i = 0; i < Size / 4; i++) + for (int32 i = 0; i < Size / ByteNum; i++) { if (i % 2 == 0) { - Audio[i * 4] = 0; - Audio[i * 4 + 1] = 0; - Audio[i * 4 + 2] = 0; - Audio[i * 4 + 3] = 0; + for (int32 j = 0; j < ByteNum; j++) + { + Audio[i * ByteNum + j] = 0; + } } } break; @@ -112,5 +134,23 @@ bool FSoundThread::CopyAudio(const uint8* Data, int32 Size, ESoundSolveType Solv bool FSoundThread::SeekFrame(int32 SeekFrameLoc) { SeekedFrame = SeekFrameLoc; + + // for (int32 i = 0; i < 200; i++) + // { + // const int32 Offset = i * (SampleRate / FGlobalData::GlobalFPS) * ByteNum * 2; + // const int32 Size = SampleRate / FGlobalData::GlobalFPS; + // Pa_WriteStream(Stream, Audio.GetData() + Offset, Size); + // } + return true; } + +void FSoundThread::StartPlay() +{ + bIsPlaying = true; +} + +void FSoundThread::StopPlay() +{ + bIsPlaying = false; +} diff --git a/Source/Cut5/Interface/SoundInterface.h b/Source/Cut5/Interface/SoundInterface.h index be5671a..889d269 100644 --- a/Source/Cut5/Interface/SoundInterface.h +++ b/Source/Cut5/Interface/SoundInterface.h @@ -9,16 +9,22 @@ extern "C"{ class FSoundThread : public FRunnable { public: - FSoundThread(int32 OutputChannel, int32 SampleRate); + FSoundThread(int32 OutputChannel, int32 SampleRate, int32 ByteNum, PaSampleFormat PaSoundType); virtual bool Init(); virtual void Exit() override; virtual uint32 Run() override; virtual void Stop() override; - + static int PaStreamCallback( + const void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); PaStream* Stream; - int32 OutputChannel; int32 SampleRate; + int32 ByteNum; + uint64 PaSoundType; bool bStoped = false; @@ -26,4 +32,8 @@ public: bool SeekFrame(int32 SeekFrameLoc); int32 SeekedFrame = 0; TArray Audio; + + void StartPlay(); + void StopPlay(); + bool bIsPlaying = false; }; diff --git a/Source/Cut5/Interface/VideoInterface.cpp b/Source/Cut5/Interface/VideoInterface.cpp new file mode 100644 index 0000000..57ef391 --- /dev/null +++ b/Source/Cut5/Interface/VideoInterface.cpp @@ -0,0 +1,177 @@ +#include "VideoInterface.h" + +#include "CutMainWidgetInterface.h" +#include "Cut5/Utils/FFMPEGUtils.h" + +bool FVideoThread::Init() +{ + FFFMPEGUtils::LoadContextPure(ClipData.MoviePath, &NewPropertyData); + if (NewPropertyData.VideoCodecContext == nullptr) + { + return false; + } + if (NewPropertyData.Context == nullptr) + { + return false; + } + + return true; +} + +uint32 FVideoThread::Run() +{ + while (!IsStop) + { + if (CurrentSeekingFrame != -1) + { + int32 VideoFPS = NewPropertyData.VideoCodecContext->framerate.num; + if (VideoFPS < FGlobalData::GlobalFPS) + { + const double Interval = FGlobalData::GlobalFPS / VideoFPS; + CurrentSeekingFrame = static_cast(CurrentSeekingFrame) / Interval; + } + else + { + VideoFPS = FGlobalData::GlobalFPS; + } + + const int64 Timestamp = av_rescale_q(static_cast(CurrentSeekingFrame) / static_cast(VideoFPS) * AV_TIME_BASE + , AVRational{1, AV_TIME_BASE} + , NewPropertyData.Context->streams[NewPropertyData.VideoStream]->time_base); + + + if (CurrentSeekingFrame - LastSeekFrame > 1 || CurrentSeekingFrame - LastSeekFrame < 0 || LastSeekFrame == -1) + { + av_seek_frame(NewPropertyData.Context, NewPropertyData.VideoStream, Timestamp, AVSEEK_FLAG_BACKWARD); + } + + + + AVPacket* Packet = av_packet_alloc(); + AVFrame* AllocatedFrame = av_frame_alloc(); + + av_init_packet(Packet); + avcodec_receive_frame(NewPropertyData.VideoCodecContext, AllocatedFrame); + int32 Times = 0; + + bool bNeedReadFrame = true; + if (CurrentSeekingFrame == LastSeekFrame && LastSeekFrame != -1) + { + if (LastFrame != nullptr) + { + AllocatedFrame = LastFrame; + bNeedReadFrame = false; + } + + } + + bool IsFailed = false; + if (bNeedReadFrame) + { + while (av_read_frame(NewPropertyData.Context, Packet) >= 0) + { + if (Packet->stream_index == NewPropertyData.VideoStream) + { + int32 Response = avcodec_send_packet(NewPropertyData.VideoCodecContext, Packet); + if (Response < 0) + { + UE_LOG(LogTemp, Error, TEXT("Error while sending a packet to the decoder: %s"), *FString(FString::FromInt(Response))); + IsFailed = true; + } + Response = avcodec_receive_frame(NewPropertyData.VideoCodecContext, AllocatedFrame); + if (Response == AVERROR(EAGAIN) || Response == AVERROR_EOF) + { + } + else if (Response < 0) + { + UE_LOG(LogTemp, Error, TEXT("Error while receiving a frame from the decoder: %s"), *FString(FString::FromInt(Response))); + IsFailed = true; + } + if (AllocatedFrame->best_effort_timestamp >= Timestamp) + { + LastFrame = AllocatedFrame; + av_packet_unref(Packet); + break; + } + } + } + } + av_packet_unref(Packet); + + if (IsFailed == true) + { + CurrentSeekingFrame = -1; + continue;; + } + + + const AVCodecContext* VideoCodecContext = NewPropertyData.VideoCodecContext; + struct SwsContext* SwsCtx = sws_getContext( + AllocatedFrame->width, AllocatedFrame->height, VideoCodecContext->pix_fmt, + AllocatedFrame->width, AllocatedFrame->height, AV_PIX_FMT_BGRA, + SWS_BILINEAR, nullptr, nullptr, nullptr + ); + if (!SwsCtx) + { + UE_LOG(LogTemp, Error, TEXT("Error creating swsContext")); + CurrentSeekingFrame = -1; + continue;; + } + + + uint8* RawData = new uint8[AllocatedFrame->width * AllocatedFrame->height * 4]; + uint8* Dest[4] = {RawData, nullptr, nullptr, nullptr}; + const int32 DestLineSize[4] = {AllocatedFrame->width * 4, 0, 0, 0}; + sws_scale(SwsCtx, AllocatedFrame->data, AllocatedFrame->linesize, 0, AllocatedFrame->height, Dest, DestLineSize); + sws_freeContext(SwsCtx); + + int32 DataSize = AllocatedFrame->width * AllocatedFrame->height * 4; + if (AllocatedFrame->data[0] == nullptr) + { + CurrentSeekingFrame = -1; + delete RawData; + continue;; + } + + + int32 X = AllocatedFrame->width; + int32 Y = AllocatedFrame->height; + AsyncTask(ENamedThreads::GameThread, [this, X, Y, RawData]() + { + MainInterface->OnUpdateVideo(FGuid(), X, Y, RawData); + + + }); + LastFrame = av_frame_alloc(); + // av_frame_copy(LastFrame, AllocatedFrame); + LastFrame = av_frame_clone(AllocatedFrame); + av_frame_free(&AllocatedFrame); + LastSeekFrame = CurrentSeekingFrame.load(); + CurrentSeekingFrame = -1; + } + } + return 0; +} + +void FVideoThread::Stop() +{ + IsStop = true; +} + +void FVideoThread::Exit() +{ + FRunnable::Exit(); +} + +FVideoThread::FVideoThread(TFunction GameThreadCallbackFunction, + const FClipData& InitClipData, ICutMainWidgetInterface* InMainInterface) +{ + BindFunction = GameThreadCallbackFunction; + ClipData = InitClipData; + MainInterface = InMainInterface; +} + +void FVideoThread::SeekFrame(int32 Frame) +{ + CurrentSeekingFrame = Frame; +} diff --git a/Source/Cut5/Interface/VideoInterface.h b/Source/Cut5/Interface/VideoInterface.h new file mode 100644 index 0000000..060bd60 --- /dev/null +++ b/Source/Cut5/Interface/VideoInterface.h @@ -0,0 +1,23 @@ +#pragma once +#include "Cut5/Widgets/DefineGlobal.h" + +class FVideoThread final: public FRunnable +{ + virtual bool Init() override; + + virtual void Exit() override; +public: + virtual void Stop() override; + FVideoThread(TFunction GameThreadCallbackFunction, const FClipData& InitClipData, ICutMainWidgetInterface* InMainInterface); + + ICutMainWidgetInterface* MainInterface = nullptr; + void SeekFrame(int32 Frame); + virtual uint32 Run() override; + TFunction BindFunction; + FClipData ClipData; + FTimelinePropertyData NewPropertyData; + std::atomic CurrentSeekingFrame = -1; + std::atomic LastSeekFrame = -1; + AVFrame* LastFrame = nullptr; + bool IsStop = false; +}; diff --git a/Source/Cut5/MainHUD.cpp b/Source/Cut5/MainHUD.cpp index d87f547..b38212d 100644 --- a/Source/Cut5/MainHUD.cpp +++ b/Source/Cut5/MainHUD.cpp @@ -13,7 +13,7 @@ AMainHUD::AMainHUD() { if (GEngine && GEngine->GameViewport) { - const TSharedPtr MainWindow = SNew(SCutMainWindow); + SAssignNew(MainWindow, SCutMainWindow); GEngine->GameViewport->AddViewportWidgetContent( MainWindow.ToSharedRef() ); @@ -28,7 +28,12 @@ AMainHUD::AMainHUD() UWidgetBlueprintLibrary::SetInputMode_UIOnlyEx(UGameplayStatics::GetPlayerController(GWorld, 0), nullptr, EMouseLockMode::DoNotLock); UGameplayStatics::GetPlayerController(GWorld, 0)->bShowMouseCursor = true; - + + GEngine->GameViewport->GetWindow()->SetWindowMode(EWindowMode::Windowed); + GEngine->GameViewport->GetWindow()->GetTitleBar()->UpdateBackgroundContent(nullptr); + + + UGameplayStatics::GetPlayerController(GWorld->GetWorld(), 0)->ConsoleCommand("t.setRes 1280x720w"); } diff --git a/Source/Cut5/MainHUD.h b/Source/Cut5/MainHUD.h index c63492a..f6f6346 100644 --- a/Source/Cut5/MainHUD.h +++ b/Source/Cut5/MainHUD.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "GameFramework/HUD.h" +#include "Widgets/SCutMainWindow.h" #include "MainHUD.generated.h" /** @@ -16,4 +17,6 @@ class CUT5_API AMainHUD : public AHUD AMainHUD(); ~AMainHUD(); + + TSharedPtr MainWindow; }; diff --git a/Source/Cut5/Utils/FFMPEGUtils.cpp b/Source/Cut5/Utils/FFMPEGUtils.cpp index 23899a3..3ac27c4 100644 --- a/Source/Cut5/Utils/FFMPEGUtils.cpp +++ b/Source/Cut5/Utils/FFMPEGUtils.cpp @@ -107,18 +107,26 @@ FString FFFMPEGUtils::LoadMedia(const FString& Path, FTimelinePropertyData* Prop 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"); } + + TArray DataResult; AVPacket Packet = *av_packet_alloc(); AVFrame* Frame = av_frame_alloc(); - while (1) + + const AVSampleFormat SampleFormat = *AudioCodecContext->codec->sample_fmts; + while (true) { if (av_read_frame(FormatContext, &Packet) < 0) { @@ -127,23 +135,31 @@ FString FFFMPEGUtils::LoadMedia(const FString& Path, FTimelinePropertyData* Prop break; } } - avcodec_send_packet(AudioCodecContext, &Packet); - if (avcodec_receive_frame(AudioCodecContext, Frame) >= 0) + if (AudioCodec->id < 0x10800 && AudioCodec->id >= 0x10000) { - const uint8* Result = FUtils::ConvertTwoChannelSound2PortAudioSound(Frame->data[0], Frame->data[1], Frame->nb_samples); - if (Result != nullptr) - { - DataResult.Append(Result, Frame->nb_samples * 4 * 2); - // Pa_WriteStream(Stream, Result, Frame->nb_samples); - } - delete[] Result; + DataResult.Append(Packet.data, Packet.size); } + else + { + avcodec_send_packet(AudioCodecContext, &Packet); + if (avcodec_receive_frame(AudioCodecContext, Frame) >= 0) + { + const uint8* Result = FUtils::ConvertTwoChannelSound2PortAudioSound(Frame->data, Frame->channels, Frame->nb_samples, FUtils::GetFormatSampleBytesNum(SampleFormat)); + if (Result != nullptr) + { + DataResult.Append(Result, Frame->nb_samples * FUtils::GetFormatSampleBytesNum(SampleFormat) * Frame->channels); + } + delete[] Result; + } + } + } PropertyData->AudioData = DataResult; PropertyData->AudioCodecContext = AudioCodecContext; PropertyData->AudioCodec = AudioCodec; PropertyData->AudioSample = AudioCodecContext->sample_rate; PropertyData->AudioChannels = AudioCodecContext->channels; + PropertyData->SampleFormat = SampleFormat; } @@ -419,133 +435,183 @@ TArray FFFMPEGUtils::GetMovieBrush(FClipData* ClipData, bool Regene TArray FFFMPEGUtils::GetAudioBrush(FClipData* ClipData) { - const float TimeLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) * FGlobalData::DefaultTimeTickSpace; - - const int32 PicLength = TimeLength / 8.0; - - if (ClipData->ResourcePropertyDataPtr) - { - if (ClipData->ResourcePropertyDataPtr->AudioData.Num() == 0) - { - return {}; - } - if (ClipData->ResourcePropertyDataPtr->AudioStream == -1) - { - return {}; - } - if (ClipData->ResourcePropertyDataPtr->VideoStream != -1) - { - return {}; - } - } - else - { - return {}; - } - - int32 DownSampleSpace = 128; - TArray DownSampledData; - DownSampledData.SetNumZeroed(ClipData->ResourcePropertyDataPtr->AudioData.Num() / 4 / DownSampleSpace); - for (int32 i = 0; i < ClipData->ResourcePropertyDataPtr->AudioData.Num() / 4; i++) - { - float NewFloat = *reinterpret_cast(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * 4)); - if (i % DownSampleSpace == 0) - { - DownSampledData[i / DownSampleSpace] = NewFloat; - } - } - - - const float FFTSize = DownSampledData.Num(); - kiss_fft_cfg Cfg = kiss_fft_alloc(FFTSize, 0, nullptr, nullptr); - - TArray KissIn, KissOut; - KissIn.SetNumZeroed(FFTSize); - KissOut.SetNumZeroed(FFTSize); - - for (int32 i = 0; i < DownSampledData.Num(); i++) - { - float NewFloat = DownSampledData[i]; - KissIn[i].r = NewFloat; - KissIn[i].i = 0; - } - - kiss_fft(Cfg, KissIn.GetData(), KissOut.GetData()); - - TArray Spectrum; - Spectrum.SetNumZeroed(FFTSize); - for (int32 i = 0; i < DownSampledData.Num(); i++) - { - Spectrum[i] = sqrt(KissOut[i].r * KissOut[i].r + KissOut[i].i * KissOut[i].i); - } - - float MaxValue = 0; - float MinValue = 0; - for (int32 i = 0; i < Spectrum.Num(); i++) - { - float NewFloat = ClipData->ResourcePropertyDataPtr->AudioData[i]; - - if (NewFloat >= MaxValue) - { - MaxValue = NewFloat; - } - if (NewFloat <= MinValue) - { - MinValue = NewFloat; - } - } - - - auto Normalize = [MaxValue, MinValue](float CurrentValue) - { - return FMath::GetMappedRangeValueClamped(FVector2D(MinValue, MaxValue), FVector2D(FGlobalData::DefaultTrackHeight, 0), CurrentValue); - }; - - ClipData->AudioBrushLength.Empty(); - ClipData->AudioBrushes.Empty(); - int32 Index = 0; - int32 TotalLength = TimeLength; - float LastPoint = 0.0; - for (int32 i = 0; i < TotalLength / PicLength; i++) - { - FLinearColor RenderColor(i * (360.0 / 8.0), 1.0, 1.0); - UTextureRenderTarget2D* TextureRenderTarget2D = NewObject(); - TextureRenderTarget2D->InitCustomFormat(PicLength, int32(FGlobalData::DefaultTrackHeight), PF_B8G8R8A8, false); - TextureRenderTarget2D->UpdateResourceImmediate(); - UKismetRenderingLibrary::ClearRenderTarget2D(GWorld->GetWorld(), TextureRenderTarget2D, FLinearColor(0, 0, 0, 0)); - UCanvas* Canvas; - FVector2D Size; - FDrawToRenderTargetContext RenderTargetContext; - UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(GWorld->GetWorld(), TextureRenderTarget2D, Canvas, Size, RenderTargetContext); - - int32 StartIndex = (Index * PicLength) / 16; - - for (int32 j = 0; j < PicLength; j++) - { - float CurrentData = Spectrum[StartIndex]; - float NormalizedData = Normalize(CurrentData); - - Canvas->K2_DrawLine(FVector2D(j - 1, LastPoint), FVector2D(j, NormalizedData), 1, RenderColor.HSVToLinearRGB()); - LastPoint = NormalizedData; - StartIndex += 1; - } - - - - - - UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(GWorld->GetWorld(), RenderTargetContext); - - - FBufferArchive Buffer; - FImageUtils::ExportRenderTarget2DAsPNG(TextureRenderTarget2D, Buffer); - FFileHelper::SaveArrayToFile(Buffer, *FPaths::Combine(FUtils::GetProjectTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png")); - TextureRenderTarget2D->MarkAsGarbage(); - FSlateDynamicImageBrush SlateBrush = FSlateDynamicImageBrush(*FPaths::Combine(FUtils::GetProjectTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png"), FVector2D(PicLength, FGlobalData::DefaultTrackHeight)); - ClipData->AudioBrushLength.Add(PicLength); - ClipData->AudioBrushes.Add(SlateBrush); - Index++; - } + // const float TimeLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) * FGlobalData::DefaultTimeTickSpace; + // + // const int32 PicLength = TimeLength / 8.0; + // + // if (ClipData->ResourcePropertyDataPtr) + // { + // if (ClipData->ResourcePropertyDataPtr->AudioData.Num() == 0) + // { + // return {}; + // } + // if (ClipData->ResourcePropertyDataPtr->AudioStream == -1) + // { + // return {}; + // } + // if (ClipData->ResourcePropertyDataPtr->VideoStream != -1) + // { + // return {}; + // } + // } + // else + // { + // return {}; + // } + // + // + // int32 DownSampleSpace = 128; + // TArray DownSampledData; + // DownSampledData.SetNumZeroed(ClipData->ResourcePropertyDataPtr->AudioData.Num() / FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat) / DownSampleSpace); + // for (int32 i = 0; i < ClipData->ResourcePropertyDataPtr->AudioData.Num() / FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat) / DownSampleSpace; i++) + // { + // switch (FUtils::GetFormatType(ClipData->ResourcePropertyDataPtr->SampleFormat)) + // { + // case 0: + // { + // uint8 NewValue = *reinterpret_cast(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat))); + // if (i % DownSampleSpace == 0) + // { + // DownSampledData[i / DownSampleSpace] = NewValue; + // } + // break; + // } + // + // case 1: + // { + // int16 NewValue = *reinterpret_cast(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat))); + // if (i % DownSampleSpace == 0) + // { + // DownSampledData[i / DownSampleSpace] = NewValue; + // } + // break; + // } + // case 2: + // { + // int32 NewValue = *reinterpret_cast(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat))); + // if (i % DownSampleSpace == 0) + // { + // DownSampledData[i / DownSampleSpace] = NewValue; + // } + // break; + // } + // case 3: + // { + // float NewValue = *reinterpret_cast(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat))); + // if (i % DownSampleSpace == 0) + // { + // DownSampledData[i / DownSampleSpace] = NewValue; + // } + // break; + // } + // case 4: + // { + // double NewValue = *reinterpret_cast(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat))); + // if (i % DownSampleSpace == 0) + // { + // DownSampledData[i / DownSampleSpace] = NewValue; + // } + // break; + // } + // default: + // break; + // } + // ; + // + // + // } + // + // + // const float FFTSize = DownSampledData.Num(); + // kiss_fft_cfg Cfg = kiss_fft_alloc(FFTSize, 0, nullptr, nullptr); + // + // TArray KissIn, KissOut; + // KissIn.SetNumZeroed(FFTSize); + // KissOut.SetNumZeroed(FFTSize); + // + // for (int32 i = 0; i < DownSampledData.Num(); i++) + // { + // float NewFloat = DownSampledData[i]; + // KissIn[i].r = NewFloat; + // KissIn[i].i = 0; + // } + // + // kiss_fft(Cfg, KissIn.GetData(), KissOut.GetData()); + // + // TArray Spectrum; + // Spectrum.SetNumZeroed(FFTSize); + // for (int32 i = 0; i < DownSampledData.Num(); i++) + // { + // Spectrum[i] = sqrt(KissOut[i].r * KissOut[i].r + KissOut[i].i * KissOut[i].i); + // } + // + // float MaxValue = 0; + // float MinValue = 0; + // for (int32 i = 0; i < Spectrum.Num(); i++) + // { + // float NewFloat = ClipData->ResourcePropertyDataPtr->AudioData[i]; + // + // if (NewFloat >= MaxValue) + // { + // MaxValue = NewFloat; + // } + // if (NewFloat <= MinValue) + // { + // MinValue = NewFloat; + // } + // } + // + // + // auto Normalize = [MaxValue, MinValue](float CurrentValue) + // { + // return FMath::GetMappedRangeValueClamped(FVector2D(MinValue, MaxValue), FVector2D(FGlobalData::DefaultTrackHeight, 0), CurrentValue); + // }; + // + // ClipData->AudioBrushLength.Empty(); + // ClipData->AudioBrushes.Empty(); + // int32 Index = 0; + // int32 TotalLength = TimeLength; + // float LastPoint = 0.0; + // for (int32 i = 0; i < TotalLength / PicLength; i++) + // { + // FLinearColor RenderColor(i * (360.0 / 8.0), 1.0, 1.0); + // UTextureRenderTarget2D* TextureRenderTarget2D = NewObject(); + // TextureRenderTarget2D->InitCustomFormat(PicLength, int32(FGlobalData::DefaultTrackHeight), PF_B8G8R8A8, false); + // TextureRenderTarget2D->UpdateResourceImmediate(); + // UKismetRenderingLibrary::ClearRenderTarget2D(GWorld->GetWorld(), TextureRenderTarget2D, FLinearColor(0, 0, 0, 0)); + // UCanvas* Canvas; + // FVector2D Size; + // FDrawToRenderTargetContext RenderTargetContext; + // UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(GWorld->GetWorld(), TextureRenderTarget2D, Canvas, Size, RenderTargetContext); + // + // int32 StartIndex = (Index * PicLength) / 16; + // + // for (int32 j = 0; j < PicLength; j++) + // { + // float CurrentData = Spectrum[StartIndex]; + // float NormalizedData = Normalize(CurrentData); + // + // Canvas->K2_DrawLine(FVector2D(j - 1, LastPoint), FVector2D(j, NormalizedData), 1, RenderColor.HSVToLinearRGB()); + // LastPoint = NormalizedData; + // StartIndex += 1; + // } + // + // + // + // + // + // UKismetRenderingLibrary::EndDrawCanvasToRenderTarget(GWorld->GetWorld(), RenderTargetContext); + // + // + // FBufferArchive Buffer; + // FImageUtils::ExportRenderTarget2DAsPNG(TextureRenderTarget2D, Buffer); + // FFileHelper::SaveArrayToFile(Buffer, *FPaths::Combine(FUtils::GetProjectTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png")); + // TextureRenderTarget2D->MarkAsGarbage(); + // FSlateDynamicImageBrush SlateBrush = FSlateDynamicImageBrush(*FPaths::Combine(FUtils::GetProjectTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png"), FVector2D(PicLength, FGlobalData::DefaultTrackHeight)); + // ClipData->AudioBrushLength.Add(PicLength); + // ClipData->AudioBrushes.Add(SlateBrush); + // Index++; + // } diff --git a/Source/Cut5/Utils/Utils.cpp b/Source/Cut5/Utils/Utils.cpp index 5bdba2d..88445b5 100644 --- a/Source/Cut5/Utils/Utils.cpp +++ b/Source/Cut5/Utils/Utils.cpp @@ -47,25 +47,28 @@ uint8* FUtils::ConvertFfMpegSound2PortAudioSound(uint8* inData[8], int32 Size) } -uint8* FUtils::ConvertTwoChannelSound2PortAudioSound(uint8* Channel1, uint8* Channel2, int32 Size) +uint8* FUtils::ConvertTwoChannelSound2PortAudioSound(uint8* Channel[], const int32 ChannelNum, const int32 Size, const int32 SampleBytes) { - void* Data = FMemory::Malloc(Size * 4 * 2); - FMemory::Memset(Data, 0, Size * 4 * 2); - if (Channel1 == nullptr) + if (Channel == nullptr) return nullptr; - if (Channel2 == nullptr) - return nullptr; - if (*Channel1 == '\0' || *Channel2 == '\0') + for (int32 i = 0; i < ChannelNum; i++) { - FMemory::Free(Data); - Data = nullptr; - return nullptr; + if (Channel[i] == nullptr) + return nullptr; + if (*Channel[i] == '\0') + return nullptr; } + + void* Data = FMemory::Malloc(Size * SampleBytes * ChannelNum); + FMemory::Memset(Data, 0, Size * SampleBytes * ChannelNum); + float* DataPtr = static_cast(Data); for (int32 i = 0; i < Size; i++) { - *(DataPtr + i * 2) = *(reinterpret_cast(Channel1) + i); - *(DataPtr + i * 2 + 1) = *(reinterpret_cast(Channel2) + i); + for (int32 j = 0; j < ChannelNum; j++) + { + *(DataPtr + i * ChannelNum + j) = *(reinterpret_cast(Channel[j]) + i); + } } return static_cast(Data); @@ -92,6 +95,150 @@ FSlateDynamicImageBrush* FUtils::GetBrushFromImage(const FString& ImageName, con return Brush; } +TArray FUtils::SingleColor2ColorArray(FColor Color) +{ + + int32 i = FGlobalData::LightArrayX * FGlobalData::LightArrayY; + TArray ColorArray; + while (i--) + { + ColorArray.Add(Color); + } + return ColorArray; +} + +int32 FUtils::GetFormatSampleBytesNum(const AVSampleFormat Format) +{ + if (Format == AV_SAMPLE_FMT_U8 || Format == AV_SAMPLE_FMT_U8P) + { + return 1; + } + else if (Format == AV_SAMPLE_FMT_S16 || Format == AV_SAMPLE_FMT_S16P) + { + return 2; + } + else if (Format == AV_SAMPLE_FMT_S32 || Format == AV_SAMPLE_FMT_S32P) + { + return 4; + } + else if (Format == AV_SAMPLE_FMT_FLT || Format == AV_SAMPLE_FMT_FLTP) + { + return 4; + } + else if (Format == AV_SAMPLE_FMT_DBL || Format == AV_SAMPLE_FMT_DBLP) + { + return 8; + } + else if (Format == AV_SAMPLE_FMT_U8P) + { + return 1; + } + else if (Format == AV_SAMPLE_FMT_S16P) + { + return 2; + } + else if (Format == AV_SAMPLE_FMT_S32P) + { + return 4; + } + else if (Format == AV_SAMPLE_FMT_FLTP) + { + return 4; + } + else if (Format == AV_SAMPLE_FMT_DBLP) + { + return 8; + } + else + { + return 0; + } +} + +int32 FUtils::GetFormatType(const AVSampleFormat Format) +{ + int32 Type = -1; + switch (Format) + { + case AV_SAMPLE_FMT_U8: + { + Type = 0; + } + break; + case AV_SAMPLE_FMT_S16: + { + Type = 1; + } + break; + case AV_SAMPLE_FMT_S32: + { + Type = 2; + } + break; + case AV_SAMPLE_FMT_FLT: + { + Type = 3; + } + break; + case AV_SAMPLE_FMT_DBL: + { + Type = 4; + } + break; + case AV_SAMPLE_FMT_U8P: + { + Type = 0; + } + break; + case AV_SAMPLE_FMT_S16P: + { + Type = 1; + } + break; + case AV_SAMPLE_FMT_S32P: + { + Type = 2; + } + break; + case AV_SAMPLE_FMT_FLTP: + { + Type = 3; + } + break; + case AV_SAMPLE_FMT_DBLP: + { + Type = 4; + } + break; + default: + { + Type = -1; + } + break; + } + return Type; +} + +FGuid FUtils::GetSoundThreadGuid(const FGuid& ClipGuid) +{ + FGuid NewGuid = ClipGuid; + NewGuid.A += 20; + NewGuid.B += 20; + NewGuid.C += 20; + NewGuid.D += 20; + return NewGuid; +} + +FGuid FUtils::GetVideoThreadGuid(const FGuid& ClipGuid) +{ + FGuid NewGuid = ClipGuid; + NewGuid.A += 40; + NewGuid.B += 40; + NewGuid.C += 40; + NewGuid.D += 40; + return NewGuid; +} + bool FUtils::DetectDragTypeCanDrop(const FClipData& DraggingType, const ETrackType& DropTrackType) { if (DropTrackType == ETrackType::LightArrayTrack || DropTrackType == ETrackType::LightBarTrack || DropTrackType == ETrackType::SpotLightTrack || DropTrackType == ETrackType::AtomSphereLightTrack) diff --git a/Source/Cut5/Utils/Utils.h b/Source/Cut5/Utils/Utils.h index d58386e..ac9fe79 100644 --- a/Source/Cut5/Utils/Utils.h +++ b/Source/Cut5/Utils/Utils.h @@ -9,13 +9,20 @@ public: // Size is nb_samples if it was float type static uint8* ConvertFfMpegSound2PortAudioSound(uint8* inData[8], int32 Size); - static uint8* ConvertTwoChannelSound2PortAudioSound(uint8* Channel1, uint8* Channel2, int32 Size); + static uint8* ConvertTwoChannelSound2PortAudioSound(uint8* Channel[], const int32 ChannelNum, const int32 Size, const int32 SampleBytes); static FString GetResourcesPath(FString ResourcesName, bool bFullPath = false); static FString GetProjectTempPath(); static FSlateDynamicImageBrush* GetBrushFromImage(const FString& ImageName, const FVector2D Size); static TArray BrushPtr; + static TArray SingleColor2ColorArray(FColor Color); + static int32 GetFormatSampleBytesNum(const AVSampleFormat Format); + static int32 GetFormatType(const AVSampleFormat Format); + template + static T* CastTypeByFormat(U* InValue, AVSampleFormat* Format); + static FGuid GetSoundThreadGuid(const FGuid& ClipGuid); + static FGuid GetVideoThreadGuid(const FGuid& ClipGuid); static bool DetectDragTypeCanDrop(const FClipData& DraggingType, const ETrackType& DropTrackType); /** @@ -82,6 +89,57 @@ public: static FString Color2Hex3(FColor Color); }; +template +T* FUtils::CastTypeByFormat(U* InValue, AVSampleFormat* Format) +{ + if (*Format == AV_SAMPLE_FMT_U8 || *Format == AV_SAMPLE_FMT_U8P) + { + return reinterpret_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_S16 || *Format == AV_SAMPLE_FMT_S16P) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_S32 || *Format == AV_SAMPLE_FMT_S32P) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_FLT || *Format == AV_SAMPLE_FMT_FLTP) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_DBL || *Format == AV_SAMPLE_FMT_DBLP) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_U8P) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_S16P) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_S32P) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_FLTP) + { + return static_cast(InValue); + } + else if (*Format == AV_SAMPLE_FMT_DBLP) + { + return static_cast(InValue); + } + else + { + return InValue; + } +} + + + class FSaveModifier { public: diff --git a/Source/Cut5/Widgets/Commands/TimelineClipCommands.cpp b/Source/Cut5/Widgets/Commands/TimelineClipCommands.cpp index 95b8b75..612474f 100644 --- a/Source/Cut5/Widgets/Commands/TimelineClipCommands.cpp +++ b/Source/Cut5/Widgets/Commands/TimelineClipCommands.cpp @@ -5,5 +5,7 @@ void FTimelineClipCommands::RegisterCommands() { UI_COMMAND(Remove, "移除", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(Break, "分割", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(Fill2Start, "填充到开头", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord()); + UI_COMMAND(Fill2End, "填充到结尾", "Executes My TimelineClipCommands", EUserInterfaceActionType::Button, FInputChord()); } #undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/Cut5/Widgets/Commands/TimelineClipCommands.h b/Source/Cut5/Widgets/Commands/TimelineClipCommands.h index 37f4eaa..276d322 100644 --- a/Source/Cut5/Widgets/Commands/TimelineClipCommands.h +++ b/Source/Cut5/Widgets/Commands/TimelineClipCommands.h @@ -19,4 +19,6 @@ public: TSharedPtr Remove; TSharedPtr Break; + TSharedPtr Fill2Start; + TSharedPtr Fill2End; }; \ No newline at end of file diff --git a/Source/Cut5/Widgets/DefineGlobal.h b/Source/Cut5/Widgets/DefineGlobal.h index bdccef4..2eac0f4 100644 --- a/Source/Cut5/Widgets/DefineGlobal.h +++ b/Source/Cut5/Widgets/DefineGlobal.h @@ -261,7 +261,7 @@ struct CUT5_API FPresetsCustomData TArray Colors = { FLinearColor(1, 1 , 1) }; int32 Times = 1; - float Angle; + float Angle = 0.0; float Time = 0.3; EPresetCustomType PresetCustomType = EPresetCustomType::None; @@ -375,8 +375,6 @@ struct CUT5_API FClipData { VideoStartFrame += CropFrame; } - - } else { @@ -402,6 +400,7 @@ struct CUT5_API FClipData // A Ptr to Input Resource; FTimelinePropertyData* ResourcePropertyDataPtr = nullptr; FGuid ResourcePropertyGuid; + AVSampleFormat SampleFormat; // Movies FString MoviePath; @@ -509,6 +508,14 @@ struct CUT5_API FTimelinePropertyData Type = InType; IconPath = InIconPath; } + + FTimelinePropertyData(bool InCanUse) + { + CanUse = InCanUse; + Name = ""; + Type = ETrackType::None; + IconPath = ""; + } bool bIsValid = true; FString Name = ""; ETrackType Type = ETrackType::VideoTrack; @@ -526,13 +533,14 @@ struct CUT5_API FTimelinePropertyData int32 AudioStream = -1; int32 AudioSample = 0; int32 AudioChannels = 2; + AVSampleFormat SampleFormat; // Base Data FString MoviePath = ""; int32 MovieFrameLength = 0; TArray AudioData; - + bool CanUse = true; bool bIsCustomPresetData = false; FPresetsCustomData PresetsCustomData = FPresetsCustomData(); @@ -994,7 +1002,7 @@ struct FProperties struct FEncodeVideoInfo { - FString EncodedVideoName = ".mp4"; + FString EncodedVideoName = ""; FString EncodedVideoTimeCode = "00:00:00:00"; int32 ClipStartFrame = 0; int32 ClipEndFrame = 0; diff --git a/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp b/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp index e80d159..632ed77 100644 --- a/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp +++ b/Source/Cut5/Widgets/DragDropOperator/DragDropOperator.cpp @@ -25,7 +25,7 @@ void DragDropOperator::UpdateClipProcess(ICutMainWidgetInterface* MainInterface, { if (!MainInterface) return; - + int32 MaxLength = 0; for (FSingleTrackGroupInstance& SingleTrackGroupInstance : MainInterface->GetCutTimeline()->TrackGroupInstances) { TSharedPtr TrackHead = StaticCastSharedPtr(SingleTrackGroupInstance.Head); @@ -33,6 +33,10 @@ void DragDropOperator::UpdateClipProcess(ICutMainWidgetInterface* MainInterface, for (int32 i = TrackHead->TrackData.ClipData.Num() - 1; i >= 0; i--) { + if (TrackHead->TrackData.ClipData[i].ClipEndFrame > MaxLength) + { + MaxLength = TrackHead->TrackData.ClipData[i].ClipEndFrame; + } if (TimelineClip.ClipStartFrame == TrackHead->TrackData.ClipData[i].ClipStartFrame || TimelineClip.ClipEndFrame == TrackHead->TrackData.ClipData[i].ClipEndFrame) { continue; @@ -102,6 +106,8 @@ void DragDropOperator::UpdateClipProcess(ICutMainWidgetInterface* MainInterface, TimelineClip.UpdateGradientCursor(); } + FGlobalData::TrackLength = MaxLength + 30; + MainInterface->GetCutTimeline()->UpdateTimelineLength(); return; } @@ -518,6 +524,9 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& CloseCursorDecorator(); const auto& DragDropOperation = static_cast(DragDropEvent.GetOperation().ToSharedRef().Get()); + + FSlateApplication::Get().SetKeyboardFocus(static_cast(SavedMainInterface)->AsShared()); + if (DragDropOperation.DragDropType == FCutDragDropBase::EType::ClipsMove) { TSharedPtr ClipMove = DragDropEvent.GetOperationAs(); @@ -706,14 +715,21 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& TSharedPtr TrackHead = TrackBody->TrackHead; TSharedPtr CutDragDropBase = DragDropEvent.GetOperationAs(); + if (CutDragDropBase->TrackType == ETrackType::None) + { + return; + } FTrackData NewTrackData; NewTrackData.TrackType = CutDragDropBase->TrackType; NewTrackData.TrackName = CutDragDropBase->DeviceName; - FDeviceTrack NewDeviceTrack; NewDeviceTrack.DeviceName = CutDragDropBase->DeviceName; NewDeviceTrack.DeviceType = CutDragDropBase->TrackType; - + + if (TrackHead->GroupName == TEXT("固定轨道")) + { + return; + } CutDragDropBase->MainInterface->GetCutTimeline()->AddNewDeviceToGroup(TrackHead->GroupName, NewDeviceTrack); CutDragDropBase->MainInterface->OnAddNewTrack(ETrackType::PlayerTrack); return; @@ -886,6 +902,7 @@ void DragDropOperator::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& return; } NewClipData.ClipType = TrackHead->TrackData.TrackType; + NewClipData.BindTrackGuid = TrackHead->TrackData.DeviceTrack.Guid; TSharedPtr OriginTrackBody = StaticCastSharedPtr(TimelineClip->Body); OriginTrackBody->TrackHead->TrackData.ClipData.Remove(NewClipData); TrackHead->TrackData.ClipData.Add(NewClipData); diff --git a/Source/Cut5/Widgets/MicroWidgets/SColorPanel.cpp b/Source/Cut5/Widgets/MicroWidgets/SColorPanel.cpp index 2547972..8723363 100644 --- a/Source/Cut5/Widgets/MicroWidgets/SColorPanel.cpp +++ b/Source/Cut5/Widgets/MicroWidgets/SColorPanel.cpp @@ -19,7 +19,7 @@ void SColorPanel::Construct(const FArguments& InArgs) CurrentSelectColor = ColorPtr->LinearRGBToHSV(); ColorS = ColorPtr->LinearRGBToHSV().G; FTextBlockStyle TextBlockStyle = FAppStyle::GetWidgetStyle("NormalText"); - TextBlockStyle.SetFontSize(16); + TextBlockStyle.SetFontSize(13); ChildSlot [ SNew(SOverlay) diff --git a/Source/Cut5/Widgets/SCustomInputPanel.cpp b/Source/Cut5/Widgets/SCustomInputPanel.cpp index 5b91249..deb8cab 100644 --- a/Source/Cut5/Widgets/SCustomInputPanel.cpp +++ b/Source/Cut5/Widgets/SCustomInputPanel.cpp @@ -324,7 +324,7 @@ void SCustomInputPanel::Construct(const FArguments& InArgs) - AddPreset(TEXT("结束后常亮"), TEXT(""), EPresetType::Custom); + // AddPreset(TEXT("结束后常亮"), TEXT(""), EPresetType::Custom); AddPreset(TEXT("渐变"), TEXT(""), EPresetType::Custom, FPresetsCustomData::EPresetCustomType::Gradient); AddPreset(TEXT("亮白"), TEXT("亮白.dat"), EPresetType::Custom); AddPreset(TEXT("红色"), TEXT("红色.dat"), EPresetType::Custom); diff --git a/Source/Cut5/Widgets/SCutMainWindow.cpp b/Source/Cut5/Widgets/SCutMainWindow.cpp index 1c205fc..75f5695 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.cpp +++ b/Source/Cut5/Widgets/SCutMainWindow.cpp @@ -392,6 +392,7 @@ void SCutMainWindow::Construct(const FArguments& InArgs) CommandList->MapAction(FShortCutCommands::Get().PlayFrame, FExecuteAction::CreateLambda([this]() { CutTimeline->SetAutoPlay(!CutTimeline->AutoPlaying); + CutTimeline->PlayImage->SetImage(FUtils::GetBrushFromImage(FUtils::GetResourcesPath(CutTimeline->AutoPlaying ? "ColorSelectCircle.png" : "PlayButton.png"), {24, 24})); })); CommandList->MapAction(FShortCutCommands::Get().Delete, FExecuteAction::CreateLambda([this]() { @@ -566,6 +567,11 @@ void SCutMainWindow::Construct(const FArguments& InArgs) FReply SCutMainWindow::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { + if (HasMouseCapture()) + { + GEngine->GameViewport->GetWindow()->MoveWindowTo(GEngine->GameViewport->GetWindow()->GetPositionInScreen() + MouseEvent.GetCursorDelta()); + } + this->NewMouseEvent = MouseEvent; return SCompoundWidget::OnMouseMove(MyGeometry, MouseEvent); } @@ -680,12 +686,34 @@ FReply SCutMainWindow::OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& return FReply::Handled().EndDragDrop(); } +void SCutMainWindow::CloseAllThreads() +{ + for (int32 i = 0; i < Threads.Num(); i++) + { + TArray Guids; + Threads.GetKeys(Guids); + for (int32 j = 0; j < Guids.Num(); j++) + { + Threads[Guids[j]]->Stop(); + delete Threads[Guids[j]]; + Threads.Remove(Guids[j]); + } + } +} void SCutMainWindow::OnUpdateVideo(FGuid UUID, int32 X, int32 Y, uint8* RawData) { - ICutMainWidgetInterface::OnUpdateVideo(UUID, X, Y, RawData); - StatePanel->VideoPlayer->UpdateVideoData(UUID, X, Y, RawData); + if (!IsInGameThread()) + { + + } + else + { + ICutMainWidgetInterface::OnUpdateVideo(UUID, X, Y, RawData); + StatePanel->VideoPlayer->UpdateVideoData(UUID, X, Y, RawData); + } + } void SCutMainWindow::OnUpdateLightArray(const TArray& LightArray) @@ -797,7 +825,7 @@ void SCutMainWindow::OpenTimeline(const FString& TimelineName, bool NeedSaveBefo if (CutTimeline->LoadTimeline(TimelineName, TimelineInfo)) { CutTimeline->TimelineInfo = TimelineInfo; - CutTimeline->CurrentEditDebug->SetText(FText::FromString(TEXT("当前正在编辑") + TimelineInfo.CurrentOpenFullPath)); + // CutTimeline->CurrentEditDebug->SetText(FText::FromString(TEXT("当前正在编辑") + TimelineInfo.CurrentOpenFullPath)); } OnAddNewTrack(ETrackType::PlayerTrack); @@ -989,7 +1017,8 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) } } } - + + bool bIsLightBar = false; for (FDeviceTrackGroup& DeviceTrackGroup : CutTimeline->DeviceTrackGroups) { for (FDeviceTrack& TrackData : DeviceTrackGroup.DeviceTracks) @@ -1031,14 +1060,21 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) DMLight->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); DeviceID++; IDList.Add(TrackData.Guid, DeviceID); + break; } case ETrackType::LightBarTrack: { + if (bIsLightBar) + { + break; + } tinyxml2::XMLElement* LightArray = LightArrayList->InsertNewChildElement("GuangZhen"); LightArray->InsertNewChildElement("ID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(DeviceID))); LightArray->InsertNewChildElement("Name")->InsertNewText(TCHAR_TO_UTF8(*TrackData.DeviceName)); DeviceID++; IDList.Add(TrackData.Guid, DeviceID); + bIsLightBar = true; + break; } default: break; @@ -1131,7 +1167,7 @@ void SCutMainWindow::ExportProject(const FString& ExportPath) } // KeyBoard - tinyxml2::XMLElement* Keyboard = RootElement->InsertNewChildElement("KeyBoard"); + tinyxml2::XMLElement* Keyboard = DeviceList->InsertNewChildElement("KeyBoard"); { tinyxml2::XMLElement* KeyCode = Keyboard->InsertNewChildElement("KeyCode"); KeyCode->InsertNewChildElement("CTRL")->InsertNewText(""); @@ -1355,8 +1391,12 @@ void SCutMainWindow::DeleteAllAssetsInTimeline() void SCutMainWindow::PreSettingBeforeSeek() { // OnUpdateProjector(0, true); - OnUpdateVideo(FGuid::NewGuid(), 1920, 1080, nullptr); + // OnUpdateVideo(FGuid::NewGuid(), 1920, 1080, nullptr); OnUpdateSpotLight(0, FColor(255, 255, 255 ,255)); + for (int32 i = 0; i < CutTimeline->TrackGroupInstances.Num(); i++) + { + OnUpdatePlayers(CutTimeline->TrackGroupInstances[i].Body, FColor(255, 255, 255, 255)); + } } void SCutMainWindow::OpenColorPanel(FLinearColor* ColorPtr) @@ -1390,30 +1430,35 @@ ESelectMode SCutMainWindow::GetSelectedMode() return SelectMode; } +SCutMainWindow* SCutMainWindow::GetSelf() +{ + return this; +} + FReply SCutMainWindow::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { if (CommandList->ProcessCommandBindings(InKeyEvent)) { - return FReply::Handled(); + return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent); } - return FReply::Handled(); + return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent); +} + +FReply SCutMainWindow::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + + return FReply::Handled().ReleaseMouseCapture(); } FReply SCutMainWindow::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (CommandList->ProcessCommandBindings(MouseEvent)) - { - return FReply::Handled(); - } - return FReply::Handled(); + + return FReply::Handled().CaptureMouse(SharedThis(this)); } FReply SCutMainWindow::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { - if (CommandList->ProcessCommandBindings(MouseEvent)) - { - return FReply::Handled(); - } + return FReply::Handled(); } @@ -1454,7 +1499,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetDeviceElement(tinyxml2::XMLElement* Par } if (Count == 0) { - Event_List->InsertNewText(""); + tinyxml2::XMLElement* Event = Event_List->InsertNewChildElement("Event"); + Event->InsertNewChildElement("Value")->InsertNewText("0"); + Event->InsertNewChildElement("TimeCode")->InsertNewText("0"); } } } @@ -1473,7 +1520,14 @@ tinyxml2::XMLElement* SCutMainWindow::GetDeviceElement(tinyxml2::XMLElement* Par tinyxml2::XMLElement* SpeicalEffect = PlayerLight->InsertNewChildElement("Special_Effects_List"); if (TrackData.ClipData.Num() == 0) { - SpeicalEffect->InsertNewText(""); + // Default + tinyxml2::XMLElement* NewSpeicalEffect = SpeicalEffect->InsertNewChildElement("Special_Effect"); + NewSpeicalEffect->InsertNewChildElement("Mode")->InsertNewText("0"); + NewSpeicalEffect->InsertNewChildElement("InitialColor")->InsertNewText("106090"); + NewSpeicalEffect->InsertNewChildElement("EndColor")->InsertNewText("000000"); + NewSpeicalEffect->InsertNewChildElement("TimeLength")->InsertNewText("-1"); + NewSpeicalEffect->InsertNewChildElement("TimeCode")->InsertNewText("0"); + NewSpeicalEffect->InsertNewChildElement("Cycle")->InsertNewText("0"); } for (int32 k = 0; k < TrackData.ClipData.Num(); k++) { @@ -1501,6 +1555,27 @@ tinyxml2::XMLElement* SCutMainWindow::GetDeviceElement(tinyxml2::XMLElement* Par NewSpeicalEffect->InsertNewChildElement("TimeCode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipStartFrame))))); NewSpeicalEffect->InsertNewChildElement("Cycle")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%d"), TempClipData.PresetsCustomData.Times))); } + if (TempClipData.PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Gradient) + { + tinyxml2::XMLElement* NewSpeicalEffect = SpeicalEffect->InsertNewChildElement("Special_Effect"); + NewSpeicalEffect->InsertNewChildElement("Mode")->InsertNewText("0"); + NewSpeicalEffect->InsertNewChildElement("InitialColor")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::Color2Hex3(TempClipData.PresetsCustomData.Cursors[0].Color.ToFColor(false))))); + NewSpeicalEffect->InsertNewChildElement("EndColor")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::Color2Hex3(TempClipData.PresetsCustomData.Cursors[1].Color.ToFColor(false))))); + float PerLength = (TempClipData.PresetsCustomData.Time / TempClipData.PresetsCustomData.Times) / 2; + NewSpeicalEffect->InsertNewChildElement("TimeLength")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%d"), int32(PerLength * 1000)))); + NewSpeicalEffect->InsertNewChildElement("TimeCode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipStartFrame))))); + NewSpeicalEffect->InsertNewChildElement("Cycle")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%d"), TempClipData.PresetsCustomData.Times))); + } + if (TempClipData.PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::None) + { + tinyxml2::XMLElement* NewSpeicalEffect = SpeicalEffect->InsertNewChildElement("Special_Effect"); + NewSpeicalEffect->InsertNewChildElement("Mode")->InsertNewText("0"); + NewSpeicalEffect->InsertNewChildElement("InitialColor")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::Color2Hex3(TempClipData.PresetsCustomData.Colors[0].ToFColor(false))))); + NewSpeicalEffect->InsertNewChildElement("EndColor")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::Color2Hex3(TempClipData.PresetsCustomData.Colors[0].ToFColor(false))))); + NewSpeicalEffect->InsertNewChildElement("TimeLength")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%d"), -1))); + NewSpeicalEffect->InsertNewChildElement("TimeCode")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%ls"), *FUtils::GetMsFromString(FGlobalData::GetTimeData(TempClipData.ClipStartFrame))))); + NewSpeicalEffect->InsertNewChildElement("Cycle")->InsertNewText(TCHAR_TO_UTF8(*FString::Printf(TEXT("%d"), 1))); + } } j++; } @@ -1584,7 +1659,15 @@ tinyxml2::XMLElement* SCutMainWindow::GetVideoElement(tinyxml2::XMLElement* Pare tinyxml2::XMLElement* Video = Parent->InsertNewChildElement("Video"); tinyxml2::XMLElement* URL = Video->InsertNewChildElement("URL"); { - URL->InsertNewText(TCHAR_TO_UTF8(*(FPaths::GetBaseFilename(EncodeVideoInfo.EncodedVideoName) + TEXT(".mp4")))); + if (EncodeVideoInfo.EncodedVideoName == "") + { + URL->InsertNewText(""); + } + else + { + URL->InsertNewText(TCHAR_TO_UTF8(*(FPaths::GetBaseFilename(EncodeVideoInfo.EncodedVideoName) + TEXT(".mp4")))); + } + } tinyxml2::XMLElement* Timecode = Video->InsertNewChildElement("Timecode"); { @@ -1598,11 +1681,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetVideoElement(tinyxml2::XMLElement* Pare { Mode->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); } - tinyxml2::XMLElement* ProjectorID = Video->InsertNewChildElement("ProjectorID"); - { - ProjectorID->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(TempProjectorID))); - } - + tinyxml2::XMLElement* VolumeEventList = Video->InsertNewChildElement("VolumeEventList"); { tinyxml2::XMLElement* VolumeEvent = VolumeEventList->InsertNewChildElement("VolumeEvent"); @@ -1625,6 +1704,13 @@ tinyxml2::XMLElement* SCutMainWindow::GetVideoElement(tinyxml2::XMLElement* Pare } } + + tinyxml2::XMLElement* ProjectorID = Video->InsertNewChildElement("ProjectorID"); + { + ProjectorID->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(TempProjectorID))); + } + + tinyxml2::XMLElement* ProjectorEventList = Video->InsertNewChildElement("ProjectorEventList"); { @@ -1681,12 +1767,17 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundElement(tinyxml2::XMLElement* Pare tinyxml2::XMLElement* Sound = Parent->InsertNewChildElement("Sound"); { Sound->InsertNewChildElement("RotationSpeakerID")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(RotatorSpeakerIndex))); - Sound->InsertNewChildElement("URL")->InsertNewText(TCHAR_TO_UTF8(*(FPaths::GetBaseFilename(EncodeVideoInfo.EncodedVideoName) + TEXT(".mp3")))); + if (EncodeVideoInfo.EncodedVideoName == "") + { + Sound->InsertNewChildElement("URL")->InsertNewText(""); + } + else + { + Sound->InsertNewChildElement("URL")->InsertNewText(TCHAR_TO_UTF8(*(FPaths::GetBaseFilename(EncodeVideoInfo.EncodedVideoName) + TEXT(".mp3")))); + } - Sound->InsertNewChildElement("Loop")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); - Sound->InsertNewChildElement("Mode")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); - Sound->InsertNewChildElement("Round")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); - Sound->InsertNewChildElement("Timecode")->InsertNewText(TCHAR_TO_UTF8(*FUtils::GetMsFromString(EncodeVideoInfo.EncodedVideoTimeCode))); + + tinyxml2::XMLElement* VolumeEventList = Sound->InsertNewChildElement("VolumeEventList"); { tinyxml2::XMLElement* VolumeEvent = VolumeEventList->InsertNewChildElement("VolumeEvent"); @@ -1707,6 +1798,12 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundElement(tinyxml2::XMLElement* Pare } } } + + Sound->InsertNewChildElement("Loop")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); + Sound->InsertNewChildElement("Mode")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(0))); + Sound->InsertNewChildElement("Round")->InsertNewText(TCHAR_TO_UTF8(*FString::FromInt(1))); + Sound->InsertNewChildElement("Timecode")->InsertNewText(TCHAR_TO_UTF8(*FUtils::GetMsFromString(EncodeVideoInfo.EncodedVideoTimeCode))); + tinyxml2::XMLElement* RotationSpeakerEventList = Sound->InsertNewChildElement("RotationSpeakerEventList"); { tinyxml2::XMLElement* RotationSpeakerEvent = RotationSpeakerEventList->InsertNewChildElement("RotationSpeakerEvent"); @@ -1716,7 +1813,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundElement(tinyxml2::XMLElement* Pare RotationSpeakerEventTimeCode->InsertNewText("0"); - RotationSpeakerEventValue->InsertNewText("0"); + RotationSpeakerEventValue->InsertNewText("1"); } } } @@ -1772,7 +1869,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetSoundListElement(tinyxml2::XMLElement* } if (Count == 0) { - AudioList->InsertNewText(""); + GetSoundElement(AudioList, FEncodeVideoInfo()); } } return nullptr; @@ -1787,6 +1884,10 @@ tinyxml2::XMLElement* SCutMainWindow::GetProcessA(tinyxml2::XMLElement* Parent, OpenTimeline(CurtainGroup->Curtains[i].TimelineInfo.CurrentOpenFullPath, true, true); GetProcessB(ProcessA, &CurtainGroup->Curtains[i]); } + if (CurtainGroup->Curtains.Num() == 0) + { + ProcessA->InsertNewText(""); + } return ProcessA; } @@ -1805,7 +1906,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetProcessB(tinyxml2::XMLElement* Parent, // 非必须项 tinyxml2::XMLElement* Identity_SpecialEffects = ProcessB->InsertNewChildElement("Identity_SpecialEffects"); { - Identity_SpecialEffects->InsertNewText(""); + tinyxml2::XMLElement* SpecialEffects = Identity_SpecialEffects->InsertNewChildElement("SpecialEffects"); + SpecialEffects->InsertNewChildElement("PlayerID")->InsertNewText("1"); + SpecialEffects->InsertNewChildElement("SpecialEffectID")->InsertNewText(""); } tinyxml2::XMLElement* IsGlobal = ProcessB->InsertNewChildElement("IsGlobal"); { @@ -1813,7 +1916,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetProcessB(tinyxml2::XMLElement* Parent, } tinyxml2::XMLElement* State = ProcessB->InsertNewChildElement("State"); { - State->InsertNewText(""); + State->InsertNewText("0"); } tinyxml2::XMLElement* EnableCard = ProcessB->InsertNewChildElement("EnableCard"); { @@ -1896,6 +1999,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffectGroup(tinyxml2::XMLElement } tinyxml2::XMLElement* AutoNext = Effect->InsertNewChildElement("AutoNext"); AutoNext->InsertNewText("0"); + + Effect->InsertNewChildElement("TimeLength")->InsertNewText("-1"); + GetSoundListElement(Effect); GetDeviceElement(Effect); GetVideoListElement(Effect); @@ -1909,6 +2015,9 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffectGroup(tinyxml2::XMLElement { State->InsertNewText("0"); } + + GetTrigger(SpecialEffect); + return Effect; } @@ -1924,6 +2033,7 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffect(tinyxml2::XMLElement* Par } tinyxml2::XMLElement* AutoNext = Effectxml->InsertNewChildElement("AutoNext"); + Effectxml->InsertNewChildElement("TimeLength")->InsertNewText("-1"); AutoNext->InsertNewText("0"); GetSoundListElement(Effectxml); GetDeviceElement(Effectxml); @@ -1938,9 +2048,26 @@ tinyxml2::XMLElement* SCutMainWindow::GetSpecialEffect(tinyxml2::XMLElement* Par { State->InsertNewText("0"); } + + GetTrigger(SpecialEffect); return Effectxml; } +tinyxml2::XMLElement* SCutMainWindow::GetTrigger(tinyxml2::XMLElement* Parent) +{ + tinyxml2::XMLElement* Trigger = Parent->InsertNewChildElement("Trigger"); + Trigger->InsertNewChildElement("TriggerMode")->InsertNewText(""); + Trigger->InsertNewChildElement("SerialNumberList")->InsertNewChildElement("SerialNumber")->InsertNewText(""); + Trigger->InsertNewChildElement("SerialNumber")->InsertNewText(""); + tinyxml2::XMLElement* KeyCode = Trigger->InsertNewChildElement("KeyCode"); + KeyCode->InsertNewChildElement("CTRL")->InsertNewText(""); + KeyCode->InsertNewChildElement("SHIFT")->InsertNewText(""); + KeyCode->InsertNewChildElement("ALT")->InsertNewText(""); + KeyCode->InsertNewChildElement("KEY")->InsertNewText(""); + Trigger->InsertNewChildElement("ActionMode")->InsertNewText(""); + return Trigger; +} + int32 SCutMainWindow::GetTrackID(FGuid Guid) const { const int32* Index = IDList.Find(Guid); @@ -1948,5 +2075,29 @@ int32 SCutMainWindow::GetTrackID(FGuid Guid) const return NewIndex; } +FRunnable* SCutMainWindow::GetThread(const FGuid& Guid) +{ + if (!Threads.Find(Guid)) + { + return nullptr; + } + return *Threads.Find(Guid); +} + +void SCutMainWindow::AddThread(const FGuid& Guid, FRunnable* InSoundThread) +{ + if (!Threads.Find(Guid)) + { + Threads.Add(Guid, InSoundThread); + } + else + { + FRunnable* Thread = *Threads.Find(Guid); + Thread->Stop(); + delete Thread; + Threads[Guid] = InSoundThread; + } +} + END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Cut5/Widgets/SCutMainWindow.h b/Source/Cut5/Widgets/SCutMainWindow.h index 61b8629..cd69e07 100644 --- a/Source/Cut5/Widgets/SCutMainWindow.h +++ b/Source/Cut5/Widgets/SCutMainWindow.h @@ -50,8 +50,13 @@ public: virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; virtual FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; + + + + + void CloseAllThreads(); FSoundThread* SoundThread; - + float TotalTime; bool bRenderLine = false; @@ -89,10 +94,12 @@ public: virtual void OpenColorPanel(FLinearColor* ColorPtr); virtual void AddNewCustomPreset(const FString& Name, const FPresetsCustomData CustomData) override; virtual ESelectMode GetSelectedMode() override; + virtual SCutMainWindow* GetSelf() override; FPointerEvent NewMouseEvent; virtual bool SupportsKeyboardFocus() const override { return true; }; virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; + virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) 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; @@ -108,12 +115,17 @@ public: tinyxml2::XMLElement* GetSpecialEffectList(tinyxml2::XMLElement* Parent); tinyxml2::XMLElement* GetSpecialEffectGroup(tinyxml2::XMLElement* Parent, FEffectCardGroup* Group); tinyxml2::XMLElement* GetSpecialEffect(tinyxml2::XMLElement* Parent, FEffectCardProperty* Effect); - + tinyxml2::XMLElement* GetTrigger(tinyxml2::XMLElement* Parent); + int32 RotatorSpeakerIndex = 0; int32 LightArrayIndex = 0; TMap IDList = {}; int32 GetTrackID(FGuid Guid) const; + + TMap Threads; + FRunnable* GetThread(const FGuid& Guid); + void AddThread(const FGuid& Guid, FRunnable* InSoundThread); }; diff --git a/Source/Cut5/Widgets/SCutTimeline.cpp b/Source/Cut5/Widgets/SCutTimeline.cpp index 7873ed0..f01d549 100644 --- a/Source/Cut5/Widgets/SCutTimeline.cpp +++ b/Source/Cut5/Widgets/SCutTimeline.cpp @@ -3,6 +3,7 @@ #include "SCutTimeline.h" +#include "SCutMainWindow.h" #include "SlateOptMacros.h" #include "STimelineTick.h" #include "STrackBody.h" @@ -91,6 +92,7 @@ int32 SCutTimeline::GetCursorPosition() const void SCutTimeline::Construct(const FArguments& InArgs) { + MainWidgetInterface = InArgs._MainWidgetInterface; ChildSlot [ @@ -150,8 +152,7 @@ void SCutTimeline::Construct(const FArguments& InArgs) + SHorizontalBox::Slot() .FillWidth(300) .AutoWidth() - - + + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) @@ -173,32 +174,50 @@ void SCutTimeline::Construct(const FArguments& InArgs) .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Center) - .SizeParam(FAuto()) [ - SNew(SImage) - .Image(FUtils::GetBrushFromImage(FUtils::GetResourcesPath("PlayButton.png"), {})) + SNew(SBox) + .WidthOverride(24) + .HeightOverride(24) + [ + SAssignNew(PlayImage, SImage) + .Image(FUtils::GetBrushFromImage(FUtils::GetResourcesPath("PlayButton.png"), {24, 24})) + .OnMouseButtonDown_Lambda([this](const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) + { + SetAutoPlay(!AutoPlaying); + PlayImage->SetImage(FUtils::GetBrushFromImage(FUtils::GetResourcesPath(AutoPlaying ? "ColorSelectCircle.png" : "PlayButton.png"), {24, 24})); + return FReply::Handled(); + }) + + ] ] + SHorizontalBox::Slot() - .SizeParam(FStretch(1.0)) [ - SAssignNew(ZoomSlider, SSlider) - .MaxValue(1.0) - .MinValue(0.0) - .Value(0.0f) - .OnValueChanged_Lambda([this](float ChangedValue) - { - FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), ChangedValue); - UpdateCursorPosition(GetCursorPosition()); - RenderGroup(); - }) - ] - + SHorizontalBox::Slot() - .SizeParam(FAuto()) - [ - SAssignNew(CurrentEditDebug, STextBlock) - .Text(FText::FromString(TEXT("当前正在编辑主面板"))) - + SNew(SBox) + .WidthOverride(200) + [ + SAssignNew(ZoomSlider, SSlider) + .MaxValue(1.0) + .MinValue(0.0) + .Value_Lambda([this]() + { + return FMath::GetMappedRangeValueClamped(FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), FVector2D(0, 1.0), FGlobalData::DefaultTimeTickSpace); + }) + .OnValueChanged_Lambda([this](float ChangedValue) + { + FGlobalData::DefaultTimeTickSpace = FMath::GetMappedRangeValueClamped(FVector2D(0, 1.0), FVector2D(GetCachedGeometry().GetLocalSize().X / FGlobalData::TrackLength, 14), ChangedValue); + UpdateCursorPosition(GetCursorPosition()); + RenderGroup(); + }) + ] + ] + // + SHorizontalBox::Slot() + // .SizeParam(FAuto()) + // [ + // SAssignNew(CurrentEditDebug, STextBlock) + // .Text(FText::FromString(TEXT("当前正在编辑主面板"))) + // + // ] ] ] @@ -366,23 +385,30 @@ void SCutTimeline::Construct(const FArguments& InArgs) AddNewDeviceToGroup(TEXT("固定轨道"), LightBarData2); FDeviceTrack SpotLightData(TEXT("投射灯一"), ETrackType::SpotLightTrack); AddNewDeviceToGroup(TEXT("固定轨道"), SpotLightData); + + + + FTickCursorTimeThread* TickCursorTimeThread = new FTickCursorTimeThread(FGlobalData::GlobalFPS, [this]() + { + UpdateCursorPosition(GetCursorPosition() + 1); + }); + FRunnableThread::Create(TickCursorTimeThread, TEXT("TickCursorTimeThread")); + } void SCutTimeline::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { - SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); + if (AutoPlaying) { TimeSpace += InDeltaTime; - if (TimeSpace >= 1.0 / FGlobalData::GlobalFPS) + if (TimeSpace >= (1.0 / FGlobalData::GlobalFPS)) { - UpdateCursorPosition(GetCursorPosition() + 1); + TimeSpace = 0.0; } - - - } + SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); } bool FSingleTrackGroupInstance::IsThis(TSharedPtr WidgetInterface) const @@ -428,6 +454,15 @@ void SCutTimeline::AddNewTrackToGroup(FString GroupName, FTrackData TrackData, E void SCutTimeline::SetAutoPlay(bool bStart) { AutoPlaying = bStart; + if (bStart) + { + ClockRun = true; + } + else + { + ClockRun = false; + } + } int32 SCutTimeline::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, @@ -833,6 +868,10 @@ void SCutTimeline::AddSelectedClipsOffset(int32 FrameOffset) ClipData->ClipStartFrame += FrameOffset; ClipData->ClipEndFrame += FrameOffset; } + for (FClipData* ClipData : NewSelectedClipData) + { + DragDropOperator::GetDragDropOperator()->UpdateClipProcess(MainWidgetInterface, *ClipData); + } RenderGroup(); } } @@ -845,6 +884,11 @@ void SCutTimeline::AddSelectedClipsOffset(int32 FrameOffset) ClipData->ClipStartFrame += FrameOffset; ClipData->ClipEndFrame += FrameOffset; } + for (FClipData* ClipData : NewSelectedClipData) + { + DragDropOperator::GetDragDropOperator()->UpdateClipProcess(MainWidgetInterface, *ClipData); + } + RenderGroup(); } } diff --git a/Source/Cut5/Widgets/SCutTimeline.h b/Source/Cut5/Widgets/SCutTimeline.h index 41806fb..0bd4605 100644 --- a/Source/Cut5/Widgets/SCutTimeline.h +++ b/Source/Cut5/Widgets/SCutTimeline.h @@ -17,6 +17,9 @@ * */ + +static inline std::atomic ClockRun = false; + struct FTrackGroup { // TrackGroup Mean Device @@ -66,8 +69,11 @@ public: void Construct(const FArguments& InArgs); virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; float TimeSpace = 0.0f; + float LastTime = 0.1f; void AddNewTrackToGroup(FString GroupName, FTrackData TrackData, ETrackType GroupType = ETrackType::None); void SetAutoPlay(bool bStart); + + FTimerHandle TimerHandle; bool AutoPlaying = false; virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; @@ -113,6 +119,7 @@ public: FTimelineInfo TimelineInfo; FGuid SelectedClipGUID; + TSharedPtr PlayImage; /** * @brief Selected Clips Guid, Use for Multi-Select. @@ -140,3 +147,45 @@ public: }; + +class FTickCursorTimeThread final : public FRunnable +{ +public: + FTickCursorTimeThread(int32 InFPS, TFunction InBindFunction) + { + FPS = InFPS; + BindFunction = InBindFunction; + } + int32 FPS = 0; + TFunction BindFunction; + bool bIsDead = false; + virtual uint32 Run() override + { + while (!bIsDead) + { + if (ClockRun) + { + FPlatformProcess::Sleep(1.0f / FPS); + FFunctionGraphTask::CreateAndDispatchWhenReady([this]() + { + if (BindFunction) + BindFunction(); + }, TStatId(), nullptr, ENamedThreads::GameThread); + } + } + return 0; + }; + virtual void Stop() override + { + bIsDead = true; + }; + virtual void Exit() override + { + + }; + virtual bool Init() override + { + return true; + }; +}; + diff --git a/Source/Cut5/Widgets/STimelineClip.cpp b/Source/Cut5/Widgets/STimelineClip.cpp index bae4d92..aac6808 100644 --- a/Source/Cut5/Widgets/STimelineClip.cpp +++ b/Source/Cut5/Widgets/STimelineClip.cpp @@ -8,17 +8,20 @@ #include "AudioDevice.h" #include "ImageUtils.h" +#include "SCutMainWindow.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/Interface/VideoInterface.h" #include "Cut5/Utils/FFMPEGUtils.h" #include "Cut5/Utils/Utils.h" #include "DragDropOperator/DragDropOperator.h" #include "Engine/Engine.h" #include "Engine/Texture2D.h" +#include "HAL/ThreadManager.h" #include "MicroWidgets/SNewProjectTips.h" #include "Presets/SClipCursor.h" #include "Rendering/DrawElementPayloads.h" @@ -48,6 +51,8 @@ FReply STimelineClip::OnBorderMouseButtonDown(const FGeometry& Geometry, const F FMenuBuilder MenuBuilder(true, CommandList); MenuBuilder.AddMenuEntry(FTimelineClipCommands::Get().Remove); MenuBuilder.AddMenuEntry(FTimelineClipCommands::Get().Break); + MenuBuilder.AddMenuEntry(FTimelineClipCommands::Get().Fill2Start); + MenuBuilder.AddMenuEntry(FTimelineClipCommands::Get().Fill2End); MenuContent = MenuBuilder.MakeWidget(); FSlateApplication::Get().PushMenu(AsShared(), FWidgetPath(), MenuContent.ToSharedRef(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect::ContextMenu); return FReply::Handled(); @@ -171,6 +176,8 @@ FReply STimelineClip::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointe return FReply::Handled(); } + + void STimelineClip::Construct(const FArguments& InArgs) { CommandList = InArgs._CommandList; @@ -202,7 +209,26 @@ void STimelineClip::Construct(const FArguments& InArgs) ]; if (ClipData->ClipType == ETrackType::AudioTrack) { - + // if (!MainWidgetInterface->GetSelf()->GetThread(ClipData->ClipGuid)) + // { + // FVideoThread* Thread = new FVideoThread([this](uint8* RawData, int32 Size, int32 X, int32 Y) + // { + // MainWidgetInterface->OnUpdateVideo(ClipData->ClipGuid, X, Y, RawData); + // }, *ClipData); + // FRunnableThread::Create(Thread, TEXT("VideoThread")); + // } + } + if (ClipData->ClipType == ETrackType::VideoTrack) + { + if (!MainWidgetInterface->GetSelf()->GetThread(FUtils::GetVideoThreadGuid(ClipData->ClipGuid))) + { + FVideoThread* Thread = new FVideoThread([this](uint8* RawData, int32 Size, int32 X, int32 Y) + { + MainWidgetInterface->OnUpdateVideo(ClipData->ClipGuid, X, Y, RawData); + }, *ClipData, MainWidgetInterface); + FRunnableThread::Create(Thread, TEXT("VideoThread")); + MainWidgetInterface->GetSelf()->AddThread(FUtils::GetVideoThreadGuid(ClipData->ClipGuid), Thread); + } } if (MainWidgetInterface->GetCutTimeline()->SelectedClipGUID == ClipData->ClipGuid) { @@ -250,96 +276,14 @@ void STimelineClip::Seek(int32 Frame) { return; } - - // FDateTime A = FDateTime::Now(); - - - - - const int32 Offset = Frame - (ClipData->ClipStartFrame); - int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset; - - int32 VideoFPS = ClipData->ResourcePropertyDataPtr->VideoCodecContext->framerate.num; - if (VideoFPS < FGlobalData::GlobalFPS) + const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset; + if (MainWidgetInterface->GetSelf()->GetThread(FUtils::GetVideoThreadGuid(ClipData->ClipGuid))) { - const double Interval = FGlobalData::GlobalFPS / VideoFPS; - SeekMovieFrame = float(SeekMovieFrame) / Interval; + static_cast(MainWidgetInterface->GetSelf()->GetThread(FUtils::GetVideoThreadGuid(ClipData->ClipGuid)))->SeekFrame(SeekMovieFrame); } - else - { - VideoFPS = FGlobalData::GlobalFPS; - } - - - int64 Timestamp = av_rescale_q(float(SeekMovieFrame) / float(VideoFPS) * AV_TIME_BASE, AVRational{1, AV_TIME_BASE}, ClipData->ResourcePropertyDataPtr->Context->streams[ClipData->ResourcePropertyDataPtr->VideoStream]->time_base); - if (VideoFPS < FGlobalData::GlobalFPS || SeekMovieFrame - LastSeekFrame > 1 || SeekMovieFrame - LastSeekFrame < 0 || LastSeekFrame == 0) - { - AVRational frame_rate = ClipData->ResourcePropertyDataPtr->Context->streams[ClipData->ResourcePropertyDataPtr->VideoStream]->avg_frame_rate; - if (av_seek_frame(ClipData->ResourcePropertyDataPtr->Context, ClipData->ResourcePropertyDataPtr->VideoStream, Timestamp, AVSEEK_FLAG_BACKWARD) < 0) - { - - }; - } - LastSeekFrame = SeekMovieFrame; - - AVPacket* Packet = av_packet_alloc(); - AVFrame* AllocatedFrame = av_frame_alloc(); - - av_init_packet(Packet); - avcodec_receive_frame(ClipData->ResourcePropertyDataPtr->VideoCodecContext, AllocatedFrame); - int32 Times = 0; - while (av_read_frame(ClipData->ResourcePropertyDataPtr->Context, Packet) >= 0) - { - if (Packet->stream_index == ClipData->ResourcePropertyDataPtr->VideoStream) - { - int32 Response = avcodec_send_packet(ClipData->ResourcePropertyDataPtr->VideoCodecContext, Packet); - if (Response < 0) - { - UE_LOG(LogTemp, Error, TEXT("Error while sending a packet to the decoder: %s"), *FString(FString::FromInt(Response))); - return; - } - Response = avcodec_receive_frame(ClipData->ResourcePropertyDataPtr->VideoCodecContext, AllocatedFrame); - if (Response == AVERROR(EAGAIN) || Response == AVERROR_EOF) - { - continue; - } - else if (Response < 0) - { - UE_LOG(LogTemp, Error, TEXT("Error while receiving a frame from the decoder: %s"), *FString(FString::FromInt(Response))); - return; - } - if (AllocatedFrame->best_effort_timestamp >= Timestamp) - { - av_packet_unref(Packet); - break; - } - } - } - av_packet_unref(Packet); - - - AVCodecContext* VideoCodecContext = ClipData->ResourcePropertyDataPtr->VideoCodecContext; - - struct SwsContext* swsCtx = sws_getContext( - AllocatedFrame->width, AllocatedFrame->height, VideoCodecContext->pix_fmt, - AllocatedFrame->width, AllocatedFrame->height, AV_PIX_FMT_BGRA, - SWS_BILINEAR, NULL, NULL, NULL - ); - if (!swsCtx) - { - UE_LOG(LogTemp, Error, TEXT("Error creating swsContext")); - return; - } - uint8* RawData = new uint8[AllocatedFrame->width * AllocatedFrame->height * 4]; - uint8* dest[4] = {RawData, 0, 0, 0}; - int32 dest_linesize[4] = {AllocatedFrame->width * 4, 0, 0, 0}; - sws_scale(swsCtx, AllocatedFrame->data, AllocatedFrame->linesize, 0, AllocatedFrame->height, dest, dest_linesize); - sws_freeContext(swsCtx); - - MainWidgetInterface->OnUpdateVideo(FGuid::NewGuid(), AllocatedFrame->width, AllocatedFrame->height, RawData); DoSound(ESoundSolveType::None, Frame); - av_frame_free(&AllocatedFrame); + LastSeekFrame = SeekMovieFrame; } break; @@ -380,116 +324,34 @@ void STimelineClip::Seek(int32 Frame) const int32 Offset = Frame - ClipData->ClipStartFrame; const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset; - + if (ClipData->LightArrayData.Num() != 0 && SeekMovieFrame < ClipData->LightArrayData.Num()) + { + MainWidgetInterface->OnUpdateLightArray(ClipData->LightArrayData[SeekMovieFrame]); + break; + } if (ClipData->PresetsCustomData.PresetCustomType != FPresetsCustomData::EPresetCustomType::None) { if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Breathe) { - // 先拿到目前位置在第几个区间 - float Between = -1; - int32 SingleAreaLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) / ClipData->PresetsCustomData.Times / 2; - if (SeekMovieFrame > 0) - { - Between = (float)SeekMovieFrame / (float)SingleAreaLength; - } - - if (Between != -1) - { - FLinearColor LinearColor = FLinearColor::Black; - FLinearColor CustomColor = ClipData->PresetsCustomData.Colors[0]; - LinearColor = FMath::Lerp((int32)Between % 2 == 0 ? CustomColor : FLinearColor::Black, (int32)Between % 2 == 0 ? FLinearColor::Black : CustomColor, Between - (int32)Between); - TArray Colors; - Colors.Init(LinearColor.ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); - MainWidgetInterface->OnUpdateLightArray(Colors); - } + MainWidgetInterface->OnUpdateLightArray(FUtils::SingleColor2ColorArray(ClipData->GetBreathColor(SeekMovieFrame))); } else if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Flash) { - float Between = -1; - int32 SingleAreaLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) / ClipData->PresetsCustomData.Times / 2; - if (SeekMovieFrame > 0) - { - Between = (float)SeekMovieFrame / (float)SingleAreaLength; - } - - if (Between != -1) - { - TArray Colors; - Colors.Init((int32)Between % 2 == 1 ? ClipData->PresetsCustomData.Colors[0].ToFColor(false) : FLinearColor::Black.ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); - MainWidgetInterface->OnUpdateLightArray(Colors); - - } - } - - - if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::None) - { - - TArray Colors; - Colors.Init(ClipData->ClipColors[0].ToFColor(false), FGlobalData::LightArrayX * FGlobalData::LightArrayY * 4); - MainWidgetInterface->OnUpdateLightArray(Colors); - break; + MainWidgetInterface->OnUpdateLightArray(FUtils::SingleColor2ColorArray(ClipData->GetFlashColor(SeekMovieFrame))); } else if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Gradient) { - int32 Between = -1; - for (int32 i = 0; i < ClipData->PresetsCustomData.Cursors.Num() - 1; i++) - { - if (SeekMovieFrame >= ClipData->PresetsCustomData.Cursors[i].CursorFrameOffset && SeekMovieFrame <= ClipData->PresetsCustomData.Cursors[i + 1].CursorFrameOffset) - { - Between = i; - } - } - if (SeekMovieFrame >= ClipData->PresetsCustomData.Cursors[ClipData->PresetsCustomData.Cursors.Num() - 1].CursorFrameOffset) - { - Between = ClipData->PresetsCustomData.Cursors.Num() - 1; - } - if (Between != -1) - { - if (Between == ClipData->PresetsCustomData.Cursors.Num() - 1) - { - TArray Colors; - Colors.Init(ClipData->PresetsCustomData.Cursors[ClipData->PresetsCustomData.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->PresetsCustomData.Cursors[Between].Color, ClipData->PresetsCustomData.Cursors[Between + 1].Color, (float)(SeekMovieFrame - ClipData->PresetsCustomData.Cursors[Between].CursorFrameOffset) / (float)(ClipData->PresetsCustomData.Cursors[Between + 1].CursorFrameOffset - ClipData->PresetsCustomData.Cursors[Between].CursorFrameOffset)).ToFColor(false); - } - MainWidgetInterface->OnUpdateLightArray(Colors); - } - - } - - break; + MainWidgetInterface->OnUpdateLightArray(FUtils::SingleColor2ColorArray(ClipData->GetGradientColor(SeekMovieFrame))); } + break; } - - - - - - if (SeekMovieFrame < ClipData->LightArrayData.Num()) + else if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::None) { - MainWidgetInterface->OnUpdateLightArray(ClipData->LightArrayData[SeekMovieFrame]); + MainWidgetInterface->OnUpdateLightArray(FUtils::SingleColor2ColorArray(ClipData->ClipColors[0].ToFColor(false))); } break; + } - case ETrackType::PlayerTrack: - { - const int32 Offset = Frame - ClipData->ClipStartFrame; - const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset; - if (SeekMovieFrame < ClipData->PlayerLightData.Num()) - { - MainWidgetInterface->OnUpdatePlayers(Body, ClipData->PlayerLightData[SeekMovieFrame]); - } - break; - } - break; case ETrackType::AudioTrack: { DoSound(ESoundSolveType::OnlyLeft, Frame); @@ -506,52 +368,25 @@ void STimelineClip::Seek(int32 Frame) { const int32 Offset = Frame - ClipData->ClipStartFrame; const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset; + + if (ClipData->PlayerLightData.Num() != 0 && SeekMovieFrame < ClipData->PlayerLightData.Num()) + { + MainWidgetInterface->OnUpdatePlayers(Body, ClipData->PlayerLightData[SeekMovieFrame]); + break; + } if (ClipData->PresetsCustomData.PresetCustomType != FPresetsCustomData::EPresetCustomType::None) { if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Breathe) { - // 先拿到目前位置在第几个区间 - float Between = -1; - int32 SingleAreaLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) / ClipData->PresetsCustomData.Times / 2; - if (SeekMovieFrame > 0) - { - Between = (float)SeekMovieFrame / (float)SingleAreaLength; - } - - if (Between != -1) - { - FLinearColor LinearColor = FLinearColor::Black; - FLinearColor CustomColor = ClipData->PresetsCustomData.Colors[0]; - LinearColor = FMath::Lerp((int32)Between % 2 == 0 ? CustomColor : FLinearColor::Black, (int32)Between % 2 == 0 ? FLinearColor::Black : CustomColor, Between - (int32)Between); - // GEngine->AddOnScreenDebugMessage(-1, 0.1f, FColor::Red, FString::Printf(TEXT("Between %s"), *CustomColor.ToString())); - MainWidgetInterface->OnUpdatePlayers(Body, LinearColor.ToFColor(false)); - } + MainWidgetInterface->OnUpdatePlayers(Body, ClipData->GetBreathColor(SeekMovieFrame)); } else if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Flash) { - float Between = -1; - int32 SingleAreaLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) / ClipData->PresetsCustomData.Times / 2; - if (SeekMovieFrame > 0) - { - Between = (float)SeekMovieFrame / (float)SingleAreaLength; - } - - if (Between != -1) - { - MainWidgetInterface->OnUpdatePlayers(Body, (int32)Between % 2 == 1 ? ClipData->PresetsCustomData.Colors[0].ToFColor(false) : FLinearColor::Black.ToFColor(false)); - } + MainWidgetInterface->OnUpdatePlayers(Body, ClipData->GetFlashColor(SeekMovieFrame)); } - - - break; - } - - - if (ClipData->PresetType == EPresetType::NotAPresets) - { - if (SeekMovieFrame < ClipData->PlayerLightData.Num()) + else if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Gradient) { - MainWidgetInterface->OnUpdatePlayers(Body, ClipData->PlayerLightData[SeekMovieFrame]); + MainWidgetInterface->OnUpdatePlayers(Body, ClipData->GetGradientColor(SeekMovieFrame)); } break; } @@ -559,35 +394,7 @@ void STimelineClip::Seek(int32 Frame) { MainWidgetInterface->OnUpdatePlayers(Body, ClipData->ClipColors[0].ToFColor(false)); } - else if (ClipData->PresetsCustomData.PresetCustomType == FPresetsCustomData::EPresetCustomType::Gradient) - { - int32 Between = -1; - for (int32 i = 0; i < ClipData->PresetsCustomData.Cursors.Num() - 1; i++) - { - if (i == 0) - { - if (SeekMovieFrame <= ClipData->PresetsCustomData.Cursors[i].CursorFrameOffset) - { - Between = i; - } - } - if (i == ClipData->PresetsCustomData.Cursors.Num() - 2) - { - if (SeekMovieFrame >= ClipData->PresetsCustomData.Cursors[i].CursorFrameOffset && SeekMovieFrame <= ClipData->PresetsCustomData.Cursors[i + 1].CursorFrameOffset) - { - Between = i; - } - } - } - if (Between != -1) - { - FLinearColor LinearColor = FLinearColor::Black; - LinearColor = FMath::Lerp(ClipData->PresetsCustomData.Cursors[Between].Color, ClipData->PresetsCustomData.Cursors[Between + 1].Color, (float)(SeekMovieFrame - ClipData->PresetsCustomData.Cursors[Between].CursorFrameOffset) / (float)(ClipData->PresetsCustomData.Cursors[Between + 1].CursorFrameOffset - ClipData->PresetsCustomData.Cursors[Between].CursorFrameOffset)); - MainWidgetInterface->OnUpdatePlayers(Body, LinearColor.ToFColor(false)); - - } - } - + break; } default: @@ -603,6 +410,16 @@ void STimelineClip::UpdatePosition(int32 StartFrame) } const int32 NewPosX = StartFrame * FGlobalData::DefaultTimeTickSpace; + if (StartFrame > 0) + { + if (ClipData->VideoStartFrame + StartFrame - ClipData->ClipStartFrame < 0) + { + return; + } + } + + + SetRenderTransform(FSlateRenderTransform(FVector2D(NewPosX, 0))); ClipDataBox->SetWidthOverride((ClipData->ClipEndFrame - StartFrame) * FGlobalData::DefaultTimeTickSpace); if (ClipData->ClipType == ETrackType::VideoTrack) @@ -630,6 +447,13 @@ void STimelineClip::UpdateLength(int32 EndFrame) { return; } + + if (ClipData->ClipType == ETrackType::VideoTrack) + { + + } + + ClipDataBox->SetWidthOverride((EndFrame - ClipData->ClipStartFrame) * FGlobalData::DefaultTimeTickSpace); if (ClipData->ClipType == ETrackType::VideoTrack) { @@ -861,22 +685,64 @@ void STimelineClip::DoSound(ESoundSolveType SolveType, int32 InFrame) TSharedPtr NewBody = StaticCastSharedPtr(Body); if (NewBody->TrackHead->TrackData.IsMute) return; - if (SoundThread == nullptr) + + + if (!static_cast(MainWidgetInterface)->GetThread(ClipData->ClipGuid)) { if (ClipData->ResourcePropertyDataPtr == nullptr) return; if (ClipData->ResourcePropertyDataPtr->AudioStream != -1) { - SoundThread = new FSoundThread(2, ClipData->ResourcePropertyDataPtr->AudioSample); + PaSampleFormat Type = paInt16; + switch (FUtils::GetFormatType(ClipData->ResourcePropertyDataPtr->SampleFormat)) + { + case 0: + { + Type = paUInt8; + break; + } + + case 1: + { + Type = paInt16; + break; + } + case 2: + { + Type = paInt32; + break; + } + case 3: + { + Type = paFloat32; + break; + } + case 4: + { + + break; + } + default: + { + Type = paInt32; + } + break; + } + SoundThread = new FSoundThread(2, ClipData->ResourcePropertyDataPtr->AudioSample, FUtils::GetFormatSampleBytesNum(ClipData->ResourcePropertyDataPtr->SampleFormat), Type); FRunnableThread* Thread = FRunnableThread::Create(SoundThread, TEXT("SoundThread")); SoundThread->CopyAudio(ClipData->ResourcePropertyDataPtr->AudioData.GetData(), ClipData->ResourcePropertyDataPtr->AudioData.Num(), SolveType); + static_cast(MainWidgetInterface)->AddThread(ClipData->ClipGuid, SoundThread); + if (MainWidgetInterface->GetCutTimeline()->AutoPlaying) + { + SoundThread->StartPlay(); + } } } const int32 Offset = InFrame - ClipData->ClipStartFrame; const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset; - if (SoundThread) + if (static_cast(MainWidgetInterface)->GetThread(ClipData->ClipGuid)) { - SoundThread->SeekFrame(SeekMovieFrame); + static_cast(MainWidgetInterface->GetSelf()->GetThread(ClipData->ClipGuid))->SeekFrame(SeekMovieFrame); } } diff --git a/Source/Cut5/Widgets/STimelineClip.h b/Source/Cut5/Widgets/STimelineClip.h index 17075b2..21a6b60 100644 --- a/Source/Cut5/Widgets/STimelineClip.h +++ b/Source/Cut5/Widgets/STimelineClip.h @@ -37,6 +37,7 @@ public: FReply OnBorderMouseButtonDown(const FGeometry& Geometry, const FPointerEvent& PointerEvent); FReply OnBorderMouseButtonUp(const FGeometry& Geometry, const FPointerEvent& PointerEvent); virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + /** Constructs this widget with InArgs */ void Construct(const FArguments& InArgs); FClipData* ClipData; @@ -55,10 +56,10 @@ public: virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; TSharedPtr ClipOverlay; TSharedPtr CommandList; - FSoundThread* SoundThread; + void DoSound(ESoundSolveType SolveType, int32 InFrame); - + FSoundThread* SoundThread = nullptr; virtual void OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; virtual void OnDragLeave(const FDragDropEvent& DragDropEvent) override; virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; @@ -82,5 +83,7 @@ public: int32 OriginClipStartFrame = 0; int32 OriginClipEndFrame = 0; + AVFrame* LastFrame = nullptr; + }; diff --git a/Source/Cut5/Widgets/STimelineProperty.cpp b/Source/Cut5/Widgets/STimelineProperty.cpp index 72bf0bb..24b512c 100644 --- a/Source/Cut5/Widgets/STimelineProperty.cpp +++ b/Source/Cut5/Widgets/STimelineProperty.cpp @@ -33,6 +33,7 @@ void STimelineProperty::Construct(const FArguments& InArgs) [ SNew(SImage) .Image(FUtils::GetBrushFromImage(FUtils::GetResourcesPath(TimelinePropertyData.IconPath), {40, 40})) + .Visibility(TimelinePropertyData.Type == ETrackType::None ? EVisibility::Collapsed : EVisibility::Visible) ] + SOverlay::Slot() diff --git a/Source/Cut5/Widgets/STimelinePropertyPanel.cpp b/Source/Cut5/Widgets/STimelinePropertyPanel.cpp index 9b5024a..9ddcdeb 100644 --- a/Source/Cut5/Widgets/STimelinePropertyPanel.cpp +++ b/Source/Cut5/Widgets/STimelinePropertyPanel.cpp @@ -151,6 +151,22 @@ void STimelinePropertyPanel::Construct(const FArguments& InArgs) ]; AddNewSelection(FTimelinePropertyData(TEXT("聚光灯"), ETrackType::SpotLightTrack, TEXT("SpotLight.png"))); AddNewSelection(FTimelinePropertyData(TEXT("氛围灯"), ETrackType::AtomSphereLightTrack, TEXT("PlayerLight.png"))); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + AddNewSelection(FTimelinePropertyData(false)); + } void STimelinePropertyPanel::AddNewSelection(FTimelinePropertyData TimelinePropertyData) diff --git a/Source/Cut5/Widgets/STrackBody.cpp b/Source/Cut5/Widgets/STrackBody.cpp index 2e7beb2..03027b8 100644 --- a/Source/Cut5/Widgets/STrackBody.cpp +++ b/Source/Cut5/Widgets/STrackBody.cpp @@ -3,6 +3,7 @@ #include "STrackBody.h" +#include "SCutMainWindow.h" #include "SlateOptMacros.h" #include "Commands/TimelineClipCommands.h" #include "Cut5/Utils/FFMPEGUtils.h" @@ -26,6 +27,14 @@ void STrackBody::Construct(const FArguments& InArgs) { BreakClip(SelectedClipGUID); }), FCanExecuteAction()); + CommandList->MapAction(FTimelineClipCommands::Get().Fill2Start, FExecuteAction::CreateLambda([this]() + { + Fill2Start(SelectedClipGUID); + }), FCanExecuteAction()); + CommandList->MapAction(FTimelineClipCommands::Get().Fill2End, FExecuteAction::CreateLambda([this]() + { + Fill2End(SelectedClipGUID); + }), FCanExecuteAction()); MainWidgetInterface = InArgs._MainWidgetInterface; TrackHead = InArgs._TrackHead; @@ -66,15 +75,7 @@ void STrackBody::Construct(const FArguments& InArgs) void STrackBody::CallRender() { Overlay->ClearChildren(); - - for (TSharedPtr Clip : SlateClips) - { - if (Clip->SoundThread) - { - Clip->SoundThread->Stop(); - } - - } + SlateClips.Empty(); for (FClipData& TempClipData : TrackHead->TrackData.ClipData) @@ -166,6 +167,39 @@ void STrackBody::RemoveClip(const FGuid& Guid) CallRender(); } + +inline void STrackBody::Fill2Start(const FGuid& Guid) +{ + for (int32 i = 0; i < SlateClips.Num(); i++) + { + if (TrackHead->TrackData.ClipData[i].ClipGuid == Guid && + TrackHead->TrackData.ClipData[i].ClipType != ETrackType::VideoTrack || + TrackHead->TrackData.ClipData[i].ClipType != ETrackType::AudioTrack || + TrackHead->TrackData.ClipData[i].ClipType != ETrackType::AudioTrackR) + { + TrackHead->TrackData.ClipData[i].ClipStartFrame = 0; + CallRender(); + break; + } + } +} + +void STrackBody::Fill2End(const FGuid& Guid) +{ + for (int32 i = 0; i < SlateClips.Num(); i++) + { + if (TrackHead->TrackData.ClipData[i].ClipGuid == Guid && + TrackHead->TrackData.ClipData[i].ClipType != ETrackType::VideoTrack || + TrackHead->TrackData.ClipData[i].ClipType != ETrackType::AudioTrack || + TrackHead->TrackData.ClipData[i].ClipType != ETrackType::AudioTrackR) + { + TrackHead->TrackData.ClipData[i].ClipEndFrame = FGlobalData::TrackLength; + CallRender(); + break; + } + } +} + void STrackBody::BreakClip(const FGuid& Guid) { for (int32 i = 0; i < SlateClips.Num(); i++) diff --git a/Source/Cut5/Widgets/STrackBody.h b/Source/Cut5/Widgets/STrackBody.h index 484c9e5..48f05e8 100644 --- a/Source/Cut5/Widgets/STrackBody.h +++ b/Source/Cut5/Widgets/STrackBody.h @@ -34,6 +34,8 @@ public: virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override; virtual void RemoveClip(const FGuid& Guid) override; virtual void BreakClip(const FGuid& Guid) override; + void Fill2Start(const FGuid& Guid); + void Fill2End(const FGuid& Guid); // virtual bool CanDragOver() override; @@ -55,9 +57,12 @@ public: }; + + + inline int32 STrackBody::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, - const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, - const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const + const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, + const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { if (bNeedRenderDragDropOver) diff --git a/Source/Cut5/Widgets/StatePanel/SLightArrayPanel.cpp b/Source/Cut5/Widgets/StatePanel/SLightArrayPanel.cpp index 8e968ee..551c419 100644 --- a/Source/Cut5/Widgets/StatePanel/SLightArrayPanel.cpp +++ b/Source/Cut5/Widgets/StatePanel/SLightArrayPanel.cpp @@ -67,7 +67,9 @@ int32 SLightArrayPanel::OnPaint(const FPaintArgs& Args, const FGeometry& Allotte // const FVector2D SingleLocalSize = // FVector2D(AllottedGeometry.AbsoluteToLocal(FVector2D(LightGrid->GetCachedGeometry().Size.X / FGlobalData::LightArrayX, LightGrid->GetCachedGeometry().Size.Y / FGlobalData::LightArrayY))); const FVector2D SingleLocalSize = - FVector2D(LightGrid->GetCachedGeometry().Size.X / FGlobalData::LightArrayX, LightGrid->GetCachedGeometry().Size.Y / FGlobalData::LightArrayY); + FVector2D((LightGrid->GetCachedGeometry().Size.X / FGlobalData::LightArrayX) / 2.0, (LightGrid->GetCachedGeometry().Size.Y / FGlobalData::LightArrayY) / 2.0); + const FVector2D OriginLocalSize = + FVector2D((LightGrid->GetCachedGeometry().Size.X / FGlobalData::LightArrayX), (LightGrid->GetCachedGeometry().Size.Y / FGlobalData::LightArrayY)); for (int32 i = 0; i < FGlobalData::LightArrayX; i++) { for (int32 j = 0; j < FGlobalData::LightArrayY; j++) @@ -76,7 +78,7 @@ int32 SLightArrayPanel::OnPaint(const FPaintArgs& Args, const FGeometry& Allotte FSlateDrawElement::MakeBox( OutDrawElements, LayerId, - LightGrid->GetPaintSpaceGeometry().ToPaintGeometry(SingleLocalSize, FSlateLayoutTransform(FVector2D(i * SingleLocalSize.X, j * SingleLocalSize.Y))), + LightGrid->GetPaintSpaceGeometry().ToPaintGeometry(SingleLocalSize, FSlateLayoutTransform(FVector2D(i * OriginLocalSize.X, j * OriginLocalSize.Y))), &Brush, ESlateDrawEffect::None, LightGridColors[j * FGlobalData::LightArrayX + i]