292 lines
8.0 KiB
C++
Raw Normal View History

2023-07-15 03:07:19 +08:00
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "FFMPEGMediaPrivate.h"
#include "IMediaCaptureSupport.h"
#include "Modules/ModuleManager.h"
#include "IFFMPEGMediaModule.h"
#include "Core.h"
#include "Interfaces/IPluginManager.h"
#include "IMediaModule.h"
#include "Modules/ModuleInterface.h"
#include "Templates/SharedPointer.h"
#if PLATFORM_ANDROID
#include "Android/AndroidJNI.h"
#endif
extern "C" {
#include "libavformat/avformat.h"
#if PLATFORM_ANDROID
#include "libavcodec/jni.h"
#endif
}
#include "FFMPEGMediaPlayer.h"
DEFINE_LOG_CATEGORY(LogFFMPEGMedia);
/**
* Implements the FFMPEGMedia module.
*/
class FFFMPEGMediaModule
: /*public IMediaCaptureSupport
,*/ public IFFMPEGMediaModule
{
public:
/** Default constructor. */
FFFMPEGMediaModule()
: Initialized(false) {
AVDeviceLibrary = nullptr;
AVFilterLibrary = nullptr;
PostProcLibrary = nullptr;
SWScaleLibrary = nullptr;
AVFormatLibrary = nullptr;
AVCodecLibrary = nullptr;
SWResampleLibrary = nullptr;
AVUtilLibrary = nullptr;
}
public:
//~ IWmfMediaModule interface
virtual TSharedPtr<IMediaPlayer, ESPMode::ThreadSafe> CreatePlayer(IMediaEventSink& EventSink) override
{
if (!Initialized)
{
return nullptr;
}
return MakeShareable(new FFFMPEGMediaPlayer(EventSink));
}
virtual TArray<FString> GetSupportedFileExtensions() override {
TMap<FString, FString> extensionMap;
void *ofmt_opaque = NULL;
const AVOutputFormat *ofmt = av_muxer_iterate(&ofmt_opaque);
while (ofmt) {
FString ext = ofmt->extensions;
TArray<FString> supportedExts;
ext.ParseIntoArray(supportedExts, TEXT(","));
for ( const FString& s: supportedExts) {
if ( extensionMap.Contains(s)) {
extensionMap[s] += TEXT(",") + FString(ofmt->name);
} else {
extensionMap.Add(s, ofmt->name );
}
}
//extensionMap.Add()
ofmt = av_muxer_iterate(&ofmt_opaque);
}
TArray<FString> extensions;
extensionMap.GetKeys(extensions);
return extensions;
}
virtual TArray<FString> GetSupportedUriSchemes() override {
void *opaque = NULL;
const char *name = avio_enum_protocols(&opaque, 1);
TArray<FString> protocols;
while (name) {
protocols.Add(name);
name = avio_enum_protocols(&opaque, 1);
}
return protocols;
}
public:
static void log_callback(void*, int level , const char* format, va_list arglist ) {
char buffer[2048];
#if PLATFORM_WINDOWS
vsprintf_s(buffer, 2048, format, arglist);
#else
vsnprintf(buffer, 2048, format, arglist);
#endif
FString str = TEXT("FFMPEG - ");
str += buffer;
switch (level) {
case AV_LOG_TRACE:
UE_LOG(LogFFMPEGMedia, VeryVerbose, TEXT("%s"), *str);
break;
case AV_LOG_DEBUG:
UE_LOG(LogFFMPEGMedia, VeryVerbose, TEXT("%s"), *str );
break;
case AV_LOG_VERBOSE:
UE_LOG(LogFFMPEGMedia, Verbose, TEXT("%s"), *str );
break;
case AV_LOG_INFO:
UE_LOG(LogFFMPEGMedia, Display, TEXT("%s"), *str );
break;
case AV_LOG_WARNING:
UE_LOG(LogFFMPEGMedia, Warning, TEXT("%s"), *str );
break;
case AV_LOG_ERROR:
UE_LOG(LogFFMPEGMedia, Error, TEXT("%s"), *str );
break;
case AV_LOG_FATAL:
UE_LOG(LogFFMPEGMedia, Fatal, TEXT("%s"), *str );
break;
default:
UE_LOG(LogFFMPEGMedia, Display, TEXT("%s"), *str );
}
}
//~ IModuleInterface interface
virtual void StartupModule() override
{
#if PLATFORM_ANDROID
UE_LOG(LogFFMPEGMedia, Verbose, TEXT("Avoid load the libraries once again on android"));
#else
AVUtilLibrary = LoadLibrary(TEXT("avutil"), TEXT("56"));
SWResampleLibrary = LoadLibrary(TEXT("swresample"), TEXT("3"));
AVCodecLibrary = LoadLibrary(TEXT("avcodec"), TEXT("58"));
AVFormatLibrary = LoadLibrary(TEXT("avformat"), TEXT("58"));
SWScaleLibrary = LoadLibrary(TEXT("swscale"), TEXT("5"));
PostProcLibrary = LoadLibrary(TEXT("postproc"), TEXT("55"));
AVFilterLibrary = LoadLibrary(TEXT("avfilter"), TEXT("7"));
AVDeviceLibrary = LoadLibrary(TEXT("avdevice"), TEXT("58"));
#endif
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100)
av_register_all();
#endif
avformat_network_init();
2023-09-04 14:16:56 +08:00
// av_log_set_level(AV_LOG_INFO);
2023-07-15 03:07:19 +08:00
2023-09-04 14:16:56 +08:00
// av_log_set_callback(&log_callback);
2023-07-15 03:07:19 +08:00
UE_LOG(LogFFMPEGMedia, Display, TEXT("FFmpeg AVCodec version: %d.%d.%d"), LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO);
UE_LOG(LogFFMPEGMedia, Display, TEXT("FFmpeg license: %s"), UTF8_TO_TCHAR(avformat_license()));
// register capture device support
auto MediaModule = FModuleManager::LoadModulePtr<IMediaModule>("Media");
if (MediaModule != nullptr)
{
//TODO: Implement Capture support
//MediaModule->RegisterCaptureSupport(*this);
} else {
UE_LOG(LogFFMPEGMedia, Error, TEXT("Coudn't find the media module"));
}
#if PLATFORM_ANDROID
// hopefully this is early enough; we don't have a way add into JNI_OnLoad in AndroidJNI.cpp
if (GJavaVM == NULL) {
UE_LOG(LogFFMPEGMedia, Error, TEXT("The global vm hasn't been initialized"));
} else {
UE_LOG(LogFFMPEGMedia, Display, TEXT("Initialize virtual machine for FFMPEG"));
av_jni_set_java_vm(GJavaVM, NULL);
}
#endif
Initialized = true;
}
virtual void ShutdownModule() override
{
if (!Initialized)
{
return;
}
// unregister capture support
auto MediaModule = FModuleManager::GetModulePtr<IMediaModule>("Media");
if (MediaModule != nullptr)
{
//MediaModule->UnregisterCaptureSupport(*this);
}
if (AVDeviceLibrary) FPlatformProcess::FreeDllHandle(AVDeviceLibrary);
if (AVFilterLibrary) FPlatformProcess::FreeDllHandle(AVFilterLibrary);
if (PostProcLibrary) FPlatformProcess::FreeDllHandle(PostProcLibrary);
if (SWScaleLibrary) FPlatformProcess::FreeDllHandle(SWScaleLibrary);
if (AVFormatLibrary) FPlatformProcess::FreeDllHandle(AVFormatLibrary);
if (AVCodecLibrary) FPlatformProcess::FreeDllHandle(AVCodecLibrary);
if (SWResampleLibrary) FPlatformProcess::FreeDllHandle(SWResampleLibrary);
if (AVUtilLibrary) FPlatformProcess::FreeDllHandle(AVUtilLibrary);
Initialized = false;
}
protected:
void* LoadLibrary(const FString& name, const FString& version) {
FString BaseDir = IPluginManager::Get().FindPlugin("FFMPEGMedia")->GetBaseDir();
FString LibDir;
FString extension;
FString prefix;
FString separator;
#if PLATFORM_MAC
LibDir = FPaths::Combine(*BaseDir, TEXT("ThirdParty/ffmpeg/lib/osx"));
extension = TEXT(".dylib");
prefix = "lib";
separator = ".";
#elif PLATFORM_WINDOWS
extension = TEXT(".dll");
prefix = "";
separator = "-";
#if PLATFORM_64BITS
LibDir = FPaths::Combine(*BaseDir, TEXT("ThirdParty/ffmpeg/bin/vs/x64"));
#else
LibDir = FPaths::Combine(*BaseDir, TEXT("ThirdParty/ffmpeg/bin/vs/win32"));
#endif
#endif
if (!LibDir.IsEmpty()) {
FString LibraryPath = FPaths::Combine(*LibDir, prefix + name + separator + version + extension);
return FPlatformProcess::GetDllHandle(*LibraryPath);
}
return nullptr;
}
private:
void* AVUtilLibrary;
void* SWResampleLibrary;
void* AVCodecLibrary;
void* SWScaleLibrary;
void* AVFormatLibrary;
void* PostProcLibrary;
void* AVFilterLibrary;
void* AVDeviceLibrary;
/** Whether the module has been initialized. */
bool Initialized;
};
IMPLEMENT_MODULE(FFFMPEGMediaModule, FFMPEGMedia);