左右声道,视频截图,进度条设计

This commit is contained in:
Sch 2023-08-21 05:10:55 +08:00
parent be067f98cc
commit 1be1ffc4cd
12 changed files with 214 additions and 40 deletions

View File

@ -2,6 +2,8 @@
#include <portaudio.h>
#include "Cut5/Widgets/DefineGlobal.h"
FCriticalSection Mutex;
FSoundThread::FSoundThread(int32 OutputChannel, int32 SampleRate) : FRunnable()
@ -33,7 +35,7 @@ uint32 FSoundThread::Run()
{
TArray<uint8, TInlineAllocator<11760>> ResultData;
const int32 FrameLength = Audio.Num();
for (int32 i = SeekedFrame * (SampleRate / 27) * 4 * 2; i < (SeekedFrame + 1) * (SampleRate / 27) * 4 * 2 && i < FrameLength; ++i)
for (int32 i = SeekedFrame * (SampleRate / 26) * 4 * 2; i < (SeekedFrame + 1) * (SampleRate / 26) * 4 * 2 && i < FrameLength; ++i)
{
ResultData.Add(Audio[i]);
}
@ -46,14 +48,55 @@ uint32 FSoundThread::Run()
void FSoundThread::Stop()
{
bStoped = true;
FRunnable::Stop();
}
bool FSoundThread::CopyAudio(const uint8* Data, int32 Size)
bool FSoundThread::CopyAudio(const uint8* Data, int32 Size, ESoundSolveType SolveType)
{
Audio = TArray<uint8>(Data, Size);
switch (SolveType)
{
case ESoundSolveType::None:
{
break;
}
case ESoundSolveType::OnlyLeft:
{
for (int32 i = 0; i < Size / 4; i++)
{
if (i % 2 == 1)
{
Audio[i * 4] = 0;
Audio[i * 4 + 1] = 0;
Audio[i * 4 + 2] = 0;
Audio[i * 4 + 3] = 0;
}
}
break;
}
case ESoundSolveType::OnlyRight:
{
for (int32 i = 0; i < Size / 4; i++)
{
if (i % 2 == 0)
{
Audio[i * 4] = 0;
Audio[i * 4 + 1] = 0;
Audio[i * 4 + 2] = 0;
Audio[i * 4 + 3] = 0;
}
}
break;
}
default:
{
break;
}
}
return true;
}

View File

@ -1,4 +1,6 @@
#pragma once
#include "Cut5/Widgets/DefineGlobal.h"
extern "C"{
#include <portaudio.h>
}
@ -20,7 +22,7 @@ public:
bool bStoped = false;
bool CopyAudio(const uint8* Data, int32 Size);
bool CopyAudio(const uint8* Data, int32 Size, ESoundSolveType SolveType);
bool SeekFrame(int32 SeekFrameLoc);
int32 SeekedFrame = 0;
TArray<uint8> Audio;

View File

@ -237,18 +237,36 @@ bool FFFMPEGUtils::ExportImage(UTexture2D* Texture2D, const FString& Path)
return FFileHelper::SaveArrayToFile(ImgData, *Path);
}
TArray<FSlateBrush> FFFMPEGUtils::GetMovieBrush(FClipData* ClipData)
TArray<FSlateBrush> FFFMPEGUtils::GetMovieBrush(FClipData* ClipData, bool Regenerate)
{
TArray<FSlateBrush> Result;
ClipData->MovieBrushes.Empty();
if (ClipData->MovieBrushesPath.Num() > 0 && !Regenerate)
{
for (int32 i = 0; i < ClipData->MovieBrushesPath.Num(); i++)
{
FSlateDynamicImageBrush Brush = FSlateDynamicImageBrush(*ClipData->MovieBrushesPath[i], FVector2f(0, 0));
Result.Add(Brush);
}
return Result;
}
for (int32 i = 0; i < ClipData->MovieBrushes.Num(); i++)
{
ClipData->MovieBrushes[i].SetResourceObject(nullptr);
}
ClipData->MovieBrushes.Empty();
ClipData->MovieBrushesPath.Empty();
TArray<FSlateBrush> Result;
if (ClipData->ResourcePropertyDataPtr)
{
const float ClipLength = (ClipData->VideoEndFrame - ClipData->VideoStartFrame) * FGlobalData::DefaultTimeTickSpace;
@ -286,36 +304,40 @@ TArray<FSlateBrush> FFFMPEGUtils::GetMovieBrush(FClipData* ClipData)
{
struct SwsContext* swsCtx = sws_getContext(
Frame->width, Frame->height, VideoCodecContext->pix_fmt,
Frame->width, Frame->height, AV_PIX_FMT_BGRA,
Frame->width / 10, Frame->height / 10, AV_PIX_FMT_BGRA,
SWS_BILINEAR, NULL, NULL, NULL
);
if (!swsCtx)
{
UE_LOG(LogTemp, Error, TEXT("Error creating swsContext"));
}
uint8* RawData = new uint8[Frame->width * Frame->height * 4];
uint8* RawData = new uint8[(Frame->width / 10) * (Frame->height / 10) * 4];
uint8* dest[4] = {RawData, 0, 0, 0};
int32 dest_linesize[4] = {Frame->width * 4, 0, 0, 0};
int32 dest_linesize[4] = {(Frame->width / 10) * 4, 0, 0, 0};
sws_scale(swsCtx, Frame->data, Frame->linesize, 0, Frame->height, dest, dest_linesize);
sws_freeContext(swsCtx);
UTexture2D* Texture = UTexture2D::CreateTransient(Frame->width, Frame->height, PF_B8G8R8A8);
UTexture2D* Texture = UTexture2D::CreateTransient(Frame->width / 10, Frame->height / 10, PF_B8G8R8A8);
if (Texture)
{
void* MipData = Texture->GetPlatformData()->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
FMemory::Memcpy(MipData, RawData, Frame->width * Frame->height * 4);
FMemory::Memcpy(MipData, RawData, (Frame->width / 10) * (Frame->height / 10) * 4);
Texture->GetPlatformData()->Mips[0].BulkData.Unlock();
Texture->UpdateResource();
// FGuid Guid = FGuid::NewGuid();
// ExportImage(Texture, *FPaths::Combine(FUtils::GetTempPath() / Guid.ToString() + ".png"));
FGuid Guid = FGuid::NewGuid();
ExportImage(Texture, *FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png"));
ClipData->MovieBrushesPath.Add(FPaths::Combine(FUtils::GetTempPath() / ClipData->ClipGuid.ToString() / Guid.ToString() + ".png"));
FSlateBrush NewBrush;
NewBrush.SetResourceObject(Texture);
Result.Add(NewBrush);
delete RawData;
}
}
}
}

View File

@ -17,6 +17,6 @@ struct FFFMPEGUtils
static FString ConvertMediaGoPto1(const FString& Path);
static bool ExportImage(UTexture2D* Texture2D, const FString& Path);
static TArray<FSlateBrush> GetMovieBrush(struct FClipData* ClipData);
static TArray<FSlateBrush> GetMovieBrush(struct FClipData* ClipData, bool Regenerate = false);
static TArray<FSlateBrush> GetAudioBrush(struct FClipData* ClipData);
};

View File

@ -270,11 +270,9 @@ TArray<FEncodeVideoInfo> FUtils::ExportPsaf(FTrackData TrackData, const FString&
FString TempPath = TempClipData.ResourcePropertyDataPtr->MoviePath;
frames.Empty();
VideoCapture capture;
bool ret = capture.open(TCHAR_TO_UTF8(*TempPath));
UE_LOG(LogTemp, Log, TEXT("cv numThreads -> %s"), *FString::FromInt(getNumThreads()));
//setNumThreads(2);
@ -309,7 +307,9 @@ TArray<FEncodeVideoInfo> FUtils::ExportPsaf(FTrackData TrackData, const FString&
Size old_size = frameSize;
capture.set(CAP_PROP_POS_FRAMES, TempClipData.ClipStartFrame);
int32 frameCount = capture.get(CV_CAP_PROP_FRAME_COUNT);
frameCount -= ClipData[i].ClipStartFrame;
UE_LOG(LogTemp, Log, TEXT("frameCount: %s"), *FString::FromInt(frameCount));
char p[128] = "pasf";

View File

@ -297,6 +297,7 @@ struct CUT5_API FClipData
Ar << ClipData.PresetType;
Ar << ClipData.bCanDrag;
Ar << ClipData.PresetsCustomData;
Ar << ClipData.MovieBrushesPath;
return Ar;
};
@ -323,6 +324,8 @@ struct CUT5_API FClipData
int32 GetClipRelativeEndFrame() const { return ClipEndFrame - ClipStartFrame; }
FPresetsCustomData PresetsCustomData;
TArray<FSlateBrush> MovieBrushes;
TArray<FString> MovieBrushesPath;
int32 MovieBrushNum;
TArray<FSlateBrush> AudioBrushes;
@ -908,3 +911,10 @@ enum class ESelectMode
SelectMode = 0,
CutMode = 1,
};
enum class ESoundSolveType
{
None,
OnlyLeft,
OnlyRight,
};

View File

@ -6,6 +6,7 @@
#include "SlateOptMacros.h"
#include "STimelineTick.h"
#include "STrackBody.h"
#include "Cut5/Utils/FFMPEGUtils.h"
#include "Cut5/Utils/Utils.h"
#include "DragDropOperator/DragDropOperator.h"
#include "MicroWidgets/SClickEditableText.h"
@ -667,6 +668,7 @@ bool SCutTimeline::LoadTimeline(const FString& LoadPath, FTimelineInfo& Info)
{
if (StaticCastSharedPtr<STrackHead>(TrackGroupInstances[l].Head)->TrackData.DeviceTrack.Guid == DeviceTrackGroups[j].DeviceTracks[k].Guid)
{
AllClips[i].MovieBrushes = FFFMPEGUtils::GetMovieBrush(&AllClips[i], false);
StaticCastSharedPtr<STrackHead>(TrackGroupInstances[l].Head)->TrackData.ClipData.Add(AllClips[i]);
}
}

View File

@ -0,0 +1,43 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "SProgressPanel.h"
#include "SlateOptMacros.h"
#include "Widgets/Layout/SBackgroundBlur.h"
#include "Widgets/Layout/SConstraintCanvas.h"
#include "Widgets/Notifications/SProgressBar.h"
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SProgressPanel::Construct(const FArguments& InArgs)
{
CurrentProgress = InArgs._Limit.X;
TotalProgress = InArgs._Limit.Y;
ChildSlot
[
SNew(SConstraintCanvas)
+ SConstraintCanvas::Slot()
[
SNew(SBackgroundBlur)
.BlurStrength(100.0)
]
+ SConstraintCanvas::Slot()
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SBox).WidthOverride(1000).HeightOverride(80)
[
SAssignNew(Progress, SProgressBar)
.Percent_Lambda([this]() {return CurrentProgress / TotalProgress; })
]
]
]
];
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION

View File

@ -0,0 +1,32 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/Notifications/SProgressBar.h"
/**
*
*/
class CUT5_API SProgressPanel : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SProgressPanel)
{
}
SLATE_ARGUMENT(FVector2D, Limit)
SLATE_END_ARGS()
/** Constructs this widget with InArgs */
void Construct(const FArguments& InArgs);
TSharedPtr<SProgressBar> Progress;
float CurrentProgress;
float TotalProgress;
void AddPercent(const float Percent) { CurrentProgress += Percent; }
void SetPercent(const float Percent) { CurrentProgress = Percent; }
float GetPercent() const { return CurrentProgress / TotalProgress; }
};

View File

@ -303,7 +303,7 @@ void STimelineClip::Seek(int32 Frame)
sws_freeContext(swsCtx);
MainWidgetInterface->OnUpdateVideo(FGuid::NewGuid(), AllocatedFrame->width, AllocatedFrame->height, RawData);
DoSound(ESoundSolveType::None, Frame);
av_frame_free(&AllocatedFrame);
}
@ -517,20 +517,13 @@ void STimelineClip::Seek(int32 Frame)
break;
case ETrackType::AudioTrack:
{
if (SoundThread == nullptr)
{
SoundThread = new FSoundThread(2, ClipData->ResourcePropertyDataPtr->AudioSample);
FRunnableThread* Thread = FRunnableThread::Create(SoundThread, TEXT("SoundThread"));
SoundThread->CopyAudio(ClipData->ResourcePropertyDataPtr->AudioData.GetData(), ClipData->ResourcePropertyDataPtr->AudioData.Num());
DoSound(ESoundSolveType::OnlyLeft, Frame);
break;
}
const int32 Offset = Frame - ClipData->ClipStartFrame;
const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset;
if (SoundThread)
case ETrackType::AudioTrackR:
{
SoundThread->SeekFrame(SeekMovieFrame);
}
DoSound(ESoundSolveType::OnlyRight, Frame);
break;
}
break;
@ -820,6 +813,22 @@ FReply STimelineClip::OnDragOver(const FGeometry& MyGeometry, const FDragDropEve
return SCompoundWidget::OnDragOver(MyGeometry, DragDropEvent);
}
void STimelineClip::DoSound(ESoundSolveType SolveType, int32 InFrame)
{
if (SoundThread == nullptr)
{
SoundThread = new FSoundThread(2, ClipData->ResourcePropertyDataPtr->AudioSample);
FRunnableThread* Thread = FRunnableThread::Create(SoundThread, TEXT("SoundThread"));
SoundThread->CopyAudio(ClipData->ResourcePropertyDataPtr->AudioData.GetData(), ClipData->ResourcePropertyDataPtr->AudioData.Num(), SolveType);
}
const int32 Offset = InFrame - ClipData->ClipStartFrame;
const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset;
if (SoundThread)
{
SoundThread->SeekFrame(SeekMovieFrame);
}
}
void STimelineClip::OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent)
{
bIsDragOver = true;

View File

@ -59,6 +59,8 @@ public:
TSharedPtr<FUICommandList> CommandList;
FSoundThread* SoundThread;
void DoSound(ESoundSolveType SolveType, int32 InFrame);
virtual void OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override;
virtual void OnDragLeave(const FDragDropEvent& DragDropEvent) override;
bool bIsDragOver = false;

View File

@ -67,6 +67,15 @@ void STrackBody::CallRender()
{
Overlay->ClearChildren();
for (TSharedPtr<STimelineClip> Clip : SlateClips)
{
if (Clip->SoundThread)
{
Clip->SoundThread->Stop();
}
}
SlateClips.Empty();
for (FClipData& TempClipData : TrackHead->TrackData.ClipData)
{
@ -178,8 +187,8 @@ void STrackBody::BreakClip(const FGuid& Guid)
if (TrackHead->TrackData.ClipData[i].ClipType == ETrackType::VideoTrack)
{
TrackHead->TrackData.ClipData[i].MovieBrushes = FFFMPEGUtils::GetMovieBrush(&TrackHead->TrackData.ClipData[i]);
TrackHead->TrackData.ClipData[Index].MovieBrushes = FFFMPEGUtils::GetMovieBrush(&NewClipData);
TrackHead->TrackData.ClipData[i].MovieBrushes = FFFMPEGUtils::GetMovieBrush(&TrackHead->TrackData.ClipData[i], true);
TrackHead->TrackData.ClipData[Index].MovieBrushes = FFFMPEGUtils::GetMovieBrush(&NewClipData, true);
}
else if (TrackHead->TrackData.ClipData[i].ClipType == ETrackType::AudioTrack || TrackHead->TrackData.ClipData[i].ClipType == ETrackType::AudioTrack)
{