左右声道,视频截图,进度条设计
This commit is contained in:
parent
be067f98cc
commit
1be1ffc4cd
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
@ -285,37 +303,41 @@ TArray<FSlateBrush> FFFMPEGUtils::GetMovieBrush(FClipData* ClipData)
|
||||
if (Frame)
|
||||
{
|
||||
struct SwsContext* swsCtx = sws_getContext(
|
||||
Frame->width, Frame->height, VideoCodecContext->pix_fmt,
|
||||
Frame->width, Frame->height, AV_PIX_FMT_BGRA,
|
||||
SWS_BILINEAR, NULL, NULL, NULL
|
||||
);
|
||||
Frame->width, Frame->height, VideoCodecContext->pix_fmt,
|
||||
Frame->width / 10, Frame->height / 10, AV_PIX_FMT_BGRA,
|
||||
SWS_BILINEAR, NULL, NULL, NULL
|
||||
);
|
||||
if (!swsCtx)
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("Error creating swsContext"));
|
||||
}
|
||||
uint8* RawData = new uint8[Frame->width * 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -269,12 +269,10 @@ TArray<FEncodeVideoInfo> FUtils::ExportPsaf(FTrackData TrackData, const FString&
|
||||
TempClipData.ResourcePropertyDataPtr->MoviePath;
|
||||
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);
|
||||
|
||||
@ -308,8 +306,10 @@ TArray<FEncodeVideoInfo> FUtils::ExportPsaf(FTrackData TrackData, const FString&
|
||||
float duration = capture.get(CV_CAP_PROP_FRAME_COUNT) / capture.get(CV_CAP_PROP_FPS);
|
||||
|
||||
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";
|
||||
|
@ -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;
|
||||
@ -907,4 +910,11 @@ enum class ESelectMode
|
||||
{
|
||||
SelectMode = 0,
|
||||
CutMode = 1,
|
||||
};
|
||||
|
||||
enum class ESoundSolveType
|
||||
{
|
||||
None,
|
||||
OnlyLeft,
|
||||
OnlyRight,
|
||||
};
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
43
Source/Cut5/Widgets/SProgressPanel.cpp
Normal file
43
Source/Cut5/Widgets/SProgressPanel.cpp
Normal 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
|
32
Source/Cut5/Widgets/SProgressPanel.h
Normal file
32
Source/Cut5/Widgets/SProgressPanel.h
Normal 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; }
|
||||
};
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
const int32 Offset = Frame - ClipData->ClipStartFrame;
|
||||
const int32 SeekMovieFrame = ClipData->VideoStartFrame + Offset;
|
||||
if (SoundThread)
|
||||
{
|
||||
SoundThread->SeekFrame(SeekMovieFrame);
|
||||
}
|
||||
DoSound(ESoundSolveType::OnlyLeft, Frame);
|
||||
break;
|
||||
}
|
||||
case ETrackType::AudioTrackR:
|
||||
{
|
||||
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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user