Files
MKCC/Plugins/VlcMedia-master/Source/VlcMedia/Private/Player/VlcMediaCallbacks.cpp
T

463 lines
12 KiB
C++

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "VlcMediaCallbacks.h"
#include "VlcMediaPrivate.h"
#include "IMediaAudioSample.h"
#include "IMediaOptions.h"
#include "IMediaTextureSample.h"
#include "MediaSamples.h"
#include "Vlc.h"
#include "VlcMediaAudioSample.h"
#include "VlcMediaTextureSample.h"
/* FVlcMediaOutput structors
*****************************************************************************/
FVlcMediaCallbacks::FVlcMediaCallbacks()
: AudioChannels(0)
, AudioSampleFormat(EMediaAudioSampleFormat::Int16)
, AudioSamplePool(new FVlcMediaAudioSamplePool)
, AudioSampleRate(0)
, AudioSampleSize(0)
, CurrentTime(FTimespan::Zero())
, Player(nullptr)
, Samples(new FMediaSamples)
, VideoBufferDim(FIntPoint::ZeroValue)
, VideoBufferStride(0)
, VideoFrameDuration(FTimespan::Zero())
, VideoOutputDim(FIntPoint::ZeroValue)
, VideoPreviousTime(FTimespan::MinValue())
, VideoSampleFormat(EMediaTextureSampleFormat::CharAYUV)
, VideoSamplePool(new FVlcMediaTextureSamplePool)
{ }
FVlcMediaCallbacks::~FVlcMediaCallbacks()
{
Shutdown();
delete AudioSamplePool;
AudioSamplePool = nullptr;
delete Samples;
Samples = nullptr;
delete VideoSamplePool;
VideoSamplePool = nullptr;
}
/* FVlcMediaOutput interface
*****************************************************************************/
IMediaSamples& FVlcMediaCallbacks::GetSamples()
{
return *Samples;
}
void FVlcMediaCallbacks::Initialize(FLibvlcMediaPlayer& InPlayer)
{
Shutdown();
Player = &InPlayer;
// register callbacks
FVlc::AudioSetFormatCallbacks(
Player,
&FVlcMediaCallbacks::StaticAudioSetupCallback,
&FVlcMediaCallbacks::StaticAudioCleanupCallback
);
FVlc::AudioSetCallbacks(
Player,
&FVlcMediaCallbacks::StaticAudioPlayCallback,
&FVlcMediaCallbacks::StaticAudioPauseCallback,
&FVlcMediaCallbacks::StaticAudioResumeCallback,
&FVlcMediaCallbacks::StaticAudioFlushCallback,
&FVlcMediaCallbacks::StaticAudioDrainCallback,
this
);
FVlc::VideoSetFormatCallbacks(
Player,
&FVlcMediaCallbacks::StaticVideoSetupCallback,
&FVlcMediaCallbacks::StaticVideoCleanupCallback
);
FVlc::VideoSetCallbacks(
Player,
&FVlcMediaCallbacks::StaticVideoLockCallback,
&FVlcMediaCallbacks::StaticVideoUnlockCallback,
&FVlcMediaCallbacks::StaticVideoDisplayCallback,
this
);
}
void FVlcMediaCallbacks::Shutdown()
{
if (Player == nullptr)
{
return;
}
// unregister callbacks
FVlc::AudioSetCallbacks(Player, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
FVlc::AudioSetFormatCallbacks(Player, nullptr, nullptr);
FVlc::VideoSetCallbacks(Player, nullptr, nullptr, nullptr, nullptr);
FVlc::VideoSetFormatCallbacks(Player, nullptr, nullptr);
AudioSamplePool->Reset();
VideoSamplePool->Reset();
CurrentTime = FTimespan::Zero();
Player = nullptr;
}
/* FVlcMediaOutput static functions
*****************************************************************************/
void FVlcMediaCallbacks::StaticAudioCleanupCallback(void* Opaque)
{
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioCleanupCallback"), Opaque);
}
void FVlcMediaCallbacks::StaticAudioDrainCallback(void* Opaque)
{
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioDrainCallback"), Opaque);
}
void FVlcMediaCallbacks::StaticAudioFlushCallback(void* Opaque, int64 Timestamp)
{
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioFlushCallback"), Opaque);
}
void FVlcMediaCallbacks::StaticAudioPauseCallback(void* Opaque, int64 Timestamp)
{
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioPauseCallback (Timestamp = %i)"), Opaque, Timestamp);
// do nothing; pausing is handled in Update
}
void FVlcMediaCallbacks::StaticAudioPlayCallback(void* Opaque, void* Samples, uint32 Count, int64 Timestamp)
{
auto Callbacks = (FVlcMediaCallbacks*)Opaque;
if (Callbacks == nullptr)
{
return;
}
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioPlayCallback (Count = %i, Timestamp = %i, Queue = %i)"),
Opaque,
Count,
Timestamp,
Callbacks->Samples->NumAudio()
);
// create & add sample to queue
auto AudioSample = Callbacks->AudioSamplePool->AcquireShared();
const FTimespan Delay = FTimespan::FromMicroseconds(FVlc::Delay(Timestamp));
const FTimespan Duration = FTimespan::FromMicroseconds((Count * 1000000) / Callbacks->AudioSampleRate);
const SIZE_T SamplesSize = Count * Callbacks->AudioSampleSize * Callbacks->AudioChannels;
if (AudioSample->Initialize(
Samples,
SamplesSize,
Count,
Callbacks->AudioChannels,
Callbacks->AudioSampleFormat,
Callbacks->AudioSampleRate,
Callbacks->CurrentTime + Delay,
Duration))
{
Callbacks->Samples->AddAudio(AudioSample);
}
}
void FVlcMediaCallbacks::StaticAudioResumeCallback(void* Opaque, int64 Timestamp)
{
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioResumeCallback (Timestamp = %i)"), Opaque, Timestamp);
// do nothing; resuming is handled in Update
}
int FVlcMediaCallbacks::StaticAudioSetupCallback(void** Opaque, ANSICHAR* Format, uint32* Rate, uint32* Channels)
{
auto Callbacks = *(FVlcMediaCallbacks**)Opaque;
if (Callbacks == nullptr)
{
return -1;
}
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticAudioSetupCallback (Format = %s, Rate = %i, Channels = %i)"),
Opaque,
ANSI_TO_TCHAR(Format),
*Rate,
*Channels
);
// setup audio format
if (*Channels > 8)
{
*Channels = 8;
}
if (FMemory::Memcmp(Format, "S8 ", 4) == 0)
{
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Int8;
Callbacks->AudioSampleSize = 1;
}
else if (FMemory::Memcmp(Format, "S16N", 4) == 0)
{
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Int16;
Callbacks->AudioSampleSize = 2;
}
else if (FMemory::Memcmp(Format, "S32N", 4) == 0)
{
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Int32;
Callbacks->AudioSampleSize = 4;
}
else if (FMemory::Memcmp(Format, "FL32", 4) == 0)
{
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Float;
Callbacks->AudioSampleSize = 4;
}
else if (FMemory::Memcmp(Format, "FL64", 4) == 0)
{
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Double;
Callbacks->AudioSampleSize = 8;
}
else if (FMemory::Memcmp(Format, "U8 ", 4) == 0)
{
// unsigned integer fall back
FMemory::Memcpy(Format, "S8 ", 4);
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Int8;
Callbacks->AudioSampleSize = 1;
}
else
{
// unsupported format fall back
FMemory::Memcpy(Format, "S16N", 4);
Callbacks->AudioSampleFormat = EMediaAudioSampleFormat::Int16;
Callbacks->AudioSampleSize = 2;
}
Callbacks->AudioChannels = *Channels;
Callbacks->AudioSampleRate = *Rate;
return 0;
}
void FVlcMediaCallbacks::StaticVideoCleanupCallback(void *Opaque)
{
// do nothing
}
void FVlcMediaCallbacks::StaticVideoDisplayCallback(void* Opaque, void* Picture)
{
auto Callbacks = (FVlcMediaCallbacks*)Opaque;
auto VideoSample = (FVlcMediaTextureSample*)Picture;
if ((Callbacks == nullptr) || (VideoSample == nullptr))
{
return;
}
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticVideoDisplayCallback (CurrentTime = %s, Queue = %i)"),
Opaque, *Callbacks->CurrentTime.ToString(),
Callbacks->Samples->NumVideoSamples()
);
VideoSample->SetTime(Callbacks->CurrentTime);
// add sample to queue
Callbacks->Samples->AddVideo(Callbacks->VideoSamplePool->ToShared(VideoSample));
}
void* FVlcMediaCallbacks::StaticVideoLockCallback(void* Opaque, void** Planes)
{
auto Callbacks = (FVlcMediaCallbacks*)Opaque;
check(Callbacks != nullptr);
FMemory::Memzero(Planes, FVlc::MaxPlanes * sizeof(void*));
// skip if already processed
if (Callbacks->VideoPreviousTime == Callbacks->CurrentTime)
{
// VLC currently requires a valid buffer or it will crash
Planes[0] = FMemory::Malloc(Callbacks->VideoBufferStride * Callbacks->VideoBufferDim.Y, 32);
return nullptr;
}
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticVideoLockCallback (CurrentTime = %s)"),
Opaque,
*Callbacks->CurrentTime.ToString()
);
// create & initialize video sample
auto VideoSample = Callbacks->VideoSamplePool->Acquire();
if (VideoSample == nullptr)
{
// VLC currently requires a valid buffer or it will crash
Planes[0] = FMemory::Malloc(Callbacks->VideoBufferStride * Callbacks->VideoBufferDim.Y, 32);
return nullptr;
}
if (!VideoSample->Initialize(
Callbacks->VideoBufferDim,
Callbacks->VideoOutputDim,
Callbacks->VideoSampleFormat,
Callbacks->VideoBufferStride,
Callbacks->VideoFrameDuration))
{
// VLC currently requires a valid buffer or it will crash
Planes[0] = FMemory::Malloc(Callbacks->VideoBufferStride * Callbacks->VideoBufferDim.Y, 32);
return nullptr;
}
Callbacks->VideoPreviousTime = Callbacks->CurrentTime;
Planes[0] = VideoSample->GetMutableBuffer();
return VideoSample; // passed as Picture into unlock & display callbacks
}
unsigned FVlcMediaCallbacks::StaticVideoSetupCallback(void** Opaque, char* Chroma, unsigned* Width, unsigned* Height, unsigned* Pitches, unsigned* Lines)
{
auto Callbacks = *(FVlcMediaCallbacks**)Opaque;
if (Callbacks == nullptr)
{
return 0;
}
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticVideoSetupCallback (Chroma = %s, Dim = %ix%i)"),
Opaque,
ANSI_TO_TCHAR(Chroma),
*Width,
*Height
);
// get video output size
if (FVlc::VideoGetSize(Callbacks->Player, 0, (uint32*)&Callbacks->VideoOutputDim.X, (uint32*)&Callbacks->VideoOutputDim.Y) != 0)
{
Callbacks->VideoBufferDim = FIntPoint::ZeroValue;
Callbacks->VideoOutputDim = FIntPoint::ZeroValue;
Callbacks->VideoBufferStride = 0;
return 0;
}
if (Callbacks->VideoOutputDim.GetMin() <= 0)
{
return 0;
}
// determine decoder & sample formats
Callbacks->VideoBufferDim = FIntPoint(*Width, *Height);
if (FCStringAnsi::Stricmp(Chroma, "AYUV") == 0)
{
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharAYUV;
Callbacks->VideoBufferStride = *Width * 4;
}
else if (FCStringAnsi::Stricmp(Chroma, "RV32") == 0)
{
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharBGRA;
Callbacks->VideoBufferStride = *Width * 4;
}
else if ((FCStringAnsi::Stricmp(Chroma, "UYVY") == 0) ||
(FCStringAnsi::Stricmp(Chroma, "Y422") == 0) ||
(FCStringAnsi::Stricmp(Chroma, "UYNV") == 0) ||
(FCStringAnsi::Stricmp(Chroma, "HDYC") == 0))
{
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharUYVY;
Callbacks->VideoBufferStride = *Width * 2;
}
else if ((FCStringAnsi::Stricmp(Chroma, "YUY2") == 0) ||
(FCStringAnsi::Stricmp(Chroma, "V422") == 0) ||
(FCStringAnsi::Stricmp(Chroma, "YUYV") == 0))
{
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharYUY2;
Callbacks->VideoBufferStride = *Width * 2;
}
else if (FCStringAnsi::Stricmp(Chroma, "YVYU") == 0)
{
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharYVYU;
Callbacks->VideoBufferStride = *Width * 2;
}
else
{
// reconfigure output for natively supported format
FLibvlcChromaDescription* ChromaDescr = FVlc::FourccGetChromaDescription(*(FLibvlcFourcc*)Chroma);
if (ChromaDescr->PlaneCount == 0)
{
return 0;
}
if (ChromaDescr->PlaneCount > 1)
{
FMemory::Memcpy(Chroma, "YUY2", 4);
Callbacks->VideoBufferDim = FIntPoint(Align(Callbacks->VideoOutputDim.X, 16) / 2, Align(Callbacks->VideoOutputDim.Y, 16));
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharYUY2;
Callbacks->VideoBufferStride = Callbacks->VideoBufferDim.X * 4;
*Height = Callbacks->VideoBufferDim.Y;
}
else
{
FMemory::Memcpy(Chroma, "RV32", 4);
Callbacks->VideoBufferDim = Callbacks->VideoOutputDim;
Callbacks->VideoSampleFormat = EMediaTextureSampleFormat::CharBGRA;
Callbacks->VideoBufferStride = Callbacks->VideoBufferDim.X * 4;
}
}
// get other video properties
//Callbacks->VideoFrameDuration = FTimespan::FromSeconds(1.0 / FVlc::MediaPlayerGetFps(Callbacks->Player));
Callbacks->VideoFrameDuration = FTimespan::FromMilliseconds(1);
// initialize decoder
Lines[0] = Callbacks->VideoBufferDim.Y;
Pitches[0] = Callbacks->VideoBufferStride;
return 1;
}
void FVlcMediaCallbacks::StaticVideoUnlockCallback(void* Opaque, void* Picture, void* const* Planes)
{
if ((Opaque != nullptr) && (Picture != nullptr))
{
UE_LOG(LogVlcMedia, VeryVerbose, TEXT("Callbacks %llx: StaticVideoUnlockCallback"), Opaque);
}
// discard temporary buffer for VLC crash workaround
if ((Picture == nullptr) && (Planes != nullptr) && (Planes[0] != nullptr))
{
FMemory::Free(Planes[0]);
}
}