mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
win-mf: Add Media Foundation AAC Encoder
Adds Microsoft Media Foundation AAC Encoder that supports 96k to 192k bitrates. This plugin is only enabled on Microsoft Windows 8+ due to performance issues found on Windows 7.
This commit is contained in:
parent
39261b8da6
commit
3ae747dd50
@ -13,6 +13,7 @@ using namespace std;
|
||||
|
||||
static const string encoders[] = {
|
||||
"ffmpeg_aac",
|
||||
"mf_aac",
|
||||
"libfdk_aac",
|
||||
"CoreAudio_AAC",
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ if(WIN32)
|
||||
add_subdirectory(win-dshow)
|
||||
add_subdirectory(win-capture)
|
||||
add_subdirectory(decklink/win)
|
||||
add_subdirectory(win-mf)
|
||||
elseif(APPLE)
|
||||
add_subdirectory(coreaudio-encoder)
|
||||
add_subdirectory(mac-avcapture)
|
||||
|
23
plugins/win-mf/CMakeLists.txt
Normal file
23
plugins/win-mf/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
project(win-mf)
|
||||
|
||||
set(win-mf_SOURCES
|
||||
mf-plugin.c
|
||||
mf-aac.cpp
|
||||
mf-aac-encoder.cpp)
|
||||
|
||||
set(win-mf_HEADERS
|
||||
mf-aac-encoder.hpp)
|
||||
|
||||
add_library(win-mf MODULE
|
||||
${win-mf_SOURCES}
|
||||
${win-mf_HEADERS})
|
||||
|
||||
target_link_libraries(win-mf
|
||||
uuid
|
||||
mfplat
|
||||
mfuuid
|
||||
mf
|
||||
wmcodecdspuuid
|
||||
libobs)
|
||||
|
||||
install_obs_plugin_with_data(win-mf data)
|
2
plugins/win-mf/data/locale/en-US.ini
Normal file
2
plugins/win-mf/data/locale/en-US.ini
Normal file
@ -0,0 +1,2 @@
|
||||
MFAACEnc="Media Foundation AAC Encoder"
|
||||
Bitrate="Bitrate"
|
346
plugins/win-mf/mf-aac-encoder.cpp
Normal file
346
plugins/win-mf/mf-aac-encoder.cpp
Normal file
@ -0,0 +1,346 @@
|
||||
#include <obs-module.h>
|
||||
|
||||
#include "mf-aac-encoder.hpp"
|
||||
|
||||
#include <mferror.h>
|
||||
#include <mftransform.h>
|
||||
#include <wmcodecdsp.h>
|
||||
#include <comdef.h>
|
||||
|
||||
#define MF_LOG_AAC(level, format, ...) \
|
||||
MF_LOG_ENCODER("AAC", ObsEncoder(), level, format, ##__VA_ARGS__)
|
||||
|
||||
#define MF_LOG_COM(msg, hr) MF_LOG_AAC(LOG_ERROR, \
|
||||
msg " failed, %S (0x%08lx)", \
|
||||
_com_error(hr).ErrorMessage(), hr)
|
||||
|
||||
#define HRC(r) \
|
||||
if(FAILED(hr = (r))) { \
|
||||
MF_LOG_COM(#r, hr); \
|
||||
goto fail; \
|
||||
}
|
||||
|
||||
using namespace MFAAC;
|
||||
|
||||
#define CONST_ARRAY(name, ...) static const UINT32 name[] = { __VA_ARGS__ };
|
||||
|
||||
CONST_ARRAY(VALID_BITRATES, 96, 128, 160, 192);
|
||||
CONST_ARRAY(VALID_CHANNELS, 1, 2);
|
||||
CONST_ARRAY(VALID_BITS_PER_SAMPLE, 16);
|
||||
CONST_ARRAY(VALID_SAMPLERATES, 44100, 48000 );
|
||||
|
||||
#undef CONST_ARRAY
|
||||
|
||||
template <int N>
|
||||
static UINT32 FindBestMatch(const UINT32 (&validValues)[N], UINT32 value)
|
||||
{
|
||||
for (UINT32 val : validValues) {
|
||||
if (val >= value)
|
||||
return val;
|
||||
}
|
||||
|
||||
// Only downgrade if no values are better
|
||||
return validValues[N - 1];
|
||||
}
|
||||
|
||||
template <int N>
|
||||
static bool IsValid(const UINT32 (&validValues)[N], UINT32 value)
|
||||
{
|
||||
for (UINT32 val : validValues) {
|
||||
if (val == value)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
UINT32 MFAAC::FindBestBitrateMatch(UINT32 value)
|
||||
{
|
||||
return FindBestMatch(VALID_BITRATES, value);
|
||||
}
|
||||
|
||||
UINT32 MFAAC::FindBestChannelsMatch(UINT32 value)
|
||||
{
|
||||
return FindBestMatch(VALID_CHANNELS, value);
|
||||
}
|
||||
|
||||
UINT32 MFAAC::FindBestBitsPerSampleMatch(UINT32 value)
|
||||
{
|
||||
return FindBestMatch(VALID_BITS_PER_SAMPLE, value);
|
||||
}
|
||||
|
||||
UINT32 MFAAC::FindBestSamplerateMatch(UINT32 value)
|
||||
{
|
||||
return FindBestMatch(VALID_SAMPLERATES, value);
|
||||
}
|
||||
|
||||
bool MFAAC::BitrateValid(UINT32 value)
|
||||
{
|
||||
return IsValid(VALID_BITRATES, value);
|
||||
}
|
||||
|
||||
bool MFAAC::ChannelsValid(UINT32 value)
|
||||
{
|
||||
return IsValid(VALID_CHANNELS, value);
|
||||
}
|
||||
|
||||
bool MFAAC::BitsPerSampleValid(UINT32 value)
|
||||
{
|
||||
return IsValid(VALID_BITS_PER_SAMPLE, value);
|
||||
}
|
||||
|
||||
bool MFAAC::SamplerateValid(UINT32 value)
|
||||
{
|
||||
return IsValid(VALID_SAMPLERATES, value);
|
||||
}
|
||||
|
||||
HRESULT MFAAC::Encoder::CreateMediaTypes(ComPtr<IMFMediaType> &i,
|
||||
ComPtr<IMFMediaType> &o)
|
||||
{
|
||||
HRESULT hr;
|
||||
HRC(MFCreateMediaType(&i));
|
||||
HRC(MFCreateMediaType(&o));
|
||||
|
||||
HRC(i->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
|
||||
HRC(i->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
|
||||
HRC(i->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample));
|
||||
HRC(i->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate));
|
||||
HRC(i->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels));
|
||||
|
||||
HRC(o->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
|
||||
HRC(o->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC));
|
||||
HRC(o->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample));
|
||||
HRC(o->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate));
|
||||
HRC(o->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels));
|
||||
HRC(o->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND,
|
||||
(bitrate * 1000) / 8));
|
||||
|
||||
return S_OK;
|
||||
fail:
|
||||
return hr;
|
||||
}
|
||||
|
||||
void MFAAC::Encoder::InitializeExtraData()
|
||||
{
|
||||
UINT16 *extraData16 = (UINT16 *)extraData;
|
||||
UINT16 profile = 2; //Low Complexity
|
||||
#define SWAPU16(x) (x>>8) | (x<<8)
|
||||
// Profile
|
||||
// XXXX X... .... ....
|
||||
*extraData16 = profile << 11;
|
||||
// Sample Index (3=48, 4=44.1)
|
||||
// .... .XXX X... ....
|
||||
*extraData16 |= sampleRate == 48000 ? 3 : 4 << 7;
|
||||
// Channels
|
||||
// .... .... .XXX X...
|
||||
*extraData16 |= channels << 3;
|
||||
*extraData16 = SWAPU16(*extraData16);
|
||||
|
||||
// Extensions
|
||||
extraData16++;
|
||||
*extraData16 = 0x2b7 << 5;
|
||||
// Profile
|
||||
*extraData16 |= profile;
|
||||
*extraData16 = SWAPU16(*extraData16);
|
||||
|
||||
extraData[4] = 0;
|
||||
#undef SWAPU16
|
||||
}
|
||||
|
||||
bool MFAAC::Encoder::Initialize()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
ComPtr<IMFTransform> transform_;
|
||||
ComPtr<IMFMediaType> inputType, outputType;
|
||||
|
||||
if (!BitrateValid(bitrate)) {
|
||||
MF_LOG_AAC(LOG_WARNING, "invalid bitrate (kbps) '%d'", bitrate);
|
||||
return false;
|
||||
}
|
||||
if (!ChannelsValid(channels)) {
|
||||
MF_LOG_AAC(LOG_WARNING, "invalid channel count '%d", channels);
|
||||
return false;
|
||||
}
|
||||
if (!SamplerateValid(sampleRate)) {
|
||||
MF_LOG_AAC(LOG_WARNING, "invalid sample rate (hz) '%d",
|
||||
sampleRate);
|
||||
return false;
|
||||
}
|
||||
if (!BitsPerSampleValid(bitsPerSample)) {
|
||||
MF_LOG_AAC(LOG_WARNING, "invalid bits-per-sample (bits) '%d'",
|
||||
bitsPerSample);
|
||||
return false;
|
||||
}
|
||||
|
||||
InitializeExtraData();
|
||||
|
||||
HRC(CoCreateInstance(CLSID_AACMFTEncoder, NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&transform_)));
|
||||
HRC(CreateMediaTypes(inputType, outputType));
|
||||
|
||||
HRC(transform_->SetInputType(0, inputType.Get(), 0));
|
||||
HRC(transform_->SetOutputType(0, outputType.Get(), 0));
|
||||
|
||||
HRC(transform_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
|
||||
NULL));
|
||||
HRC(transform_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM,
|
||||
NULL));
|
||||
|
||||
MF_LOG_AAC(LOG_INFO, "encoder created\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tchannels: %d\n"
|
||||
"\tsample rate: %d\n"
|
||||
"\tbits-per-sample: %d\n",
|
||||
bitrate, channels, sampleRate, bitsPerSample);
|
||||
|
||||
transform = transform_;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT MFAAC::Encoder::CreateEmptySample(ComPtr<IMFSample> &sample,
|
||||
ComPtr<IMFMediaBuffer> &buffer, DWORD length)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
HRC(MFCreateSample(&sample));
|
||||
HRC(MFCreateMemoryBuffer(length, &buffer));
|
||||
HRC(sample->AddBuffer(buffer.Get()));
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT MFAAC::Encoder::EnsureCapacity(ComPtr<IMFSample> &sample, DWORD length)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IMFMediaBuffer> buffer;
|
||||
DWORD currentLength;
|
||||
|
||||
if (!sample) {
|
||||
HRC(CreateEmptySample(sample, buffer, length));
|
||||
} else {
|
||||
HRC(sample->GetBufferByIndex(0, &buffer));
|
||||
}
|
||||
|
||||
HRC(buffer->GetMaxLength(¤tLength));
|
||||
if (currentLength < length) {
|
||||
HRC(sample->RemoveAllBuffers());
|
||||
HRC(MFCreateMemoryBuffer(length, &buffer));
|
||||
HRC(sample->AddBuffer(buffer));
|
||||
} else {
|
||||
buffer->SetCurrentLength(0);
|
||||
}
|
||||
|
||||
packetBuffer.reserve(length);
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
return hr;
|
||||
}
|
||||
|
||||
bool MFAAC::Encoder::ProcessInput(UINT8 *data, UINT32 data_length,
|
||||
UINT64 pts, Status *status)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IMFSample> sample;
|
||||
ComPtr<IMFMediaBuffer> buffer;
|
||||
BYTE *bufferData;
|
||||
INT64 samplePts;
|
||||
UINT32 samples;
|
||||
UINT64 sampleDur;
|
||||
|
||||
HRC(CreateEmptySample(sample, buffer, data_length));
|
||||
|
||||
HRC(buffer->Lock(&bufferData, NULL, NULL));
|
||||
memcpy(bufferData, data, data_length);
|
||||
HRC(buffer->Unlock());
|
||||
HRC(buffer->SetCurrentLength(data_length));
|
||||
|
||||
samples = data_length / channels / (bitsPerSample / 8);
|
||||
sampleDur = (UINT64)(((float) sampleRate / channels / samples) * 10000);
|
||||
samplePts = pts / 100;
|
||||
|
||||
HRC(sample->SetSampleTime(samplePts));
|
||||
HRC(sample->SetSampleDuration(sampleDur));
|
||||
|
||||
hr = transform->ProcessInput(0, sample, 0);
|
||||
if (hr == MF_E_NOTACCEPTING) {
|
||||
*status = NOT_ACCEPTING;
|
||||
return true;
|
||||
} else if (FAILED(hr)) {
|
||||
MF_LOG_COM("process input", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
*status = SUCCESS;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
*status = FAILURE;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MFAAC::Encoder::ProcessOutput(UINT8 **data, UINT32 *dataLength,
|
||||
UINT64 *pts, Status *status)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
DWORD outputFlags, outputStatus;
|
||||
MFT_OUTPUT_STREAM_INFO outputInfo = {0};
|
||||
MFT_OUTPUT_DATA_BUFFER output = {0};
|
||||
ComPtr<IMFMediaBuffer> outputBuffer;
|
||||
BYTE *bufferData;
|
||||
DWORD bufferLength;
|
||||
INT64 samplePts;
|
||||
|
||||
HRC(transform->GetOutputStatus(&outputFlags));
|
||||
if (outputFlags != MFT_OUTPUT_STATUS_SAMPLE_READY) {
|
||||
*status = NEED_MORE_INPUT;
|
||||
return true;
|
||||
}
|
||||
|
||||
HRC(transform->GetOutputStreamInfo(0, &outputInfo));
|
||||
EnsureCapacity(outputSample, outputInfo.cbSize);
|
||||
|
||||
output.pSample = outputSample.Get();
|
||||
|
||||
hr = transform->ProcessOutput(0, 1, &output, &outputStatus);
|
||||
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
|
||||
*status = NEED_MORE_INPUT;
|
||||
return true;
|
||||
} else if (FAILED(hr)) {
|
||||
MF_LOG_COM("process output", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
HRC(outputSample->GetBufferByIndex(0, &outputBuffer));
|
||||
|
||||
HRC(outputBuffer->Lock(&bufferData, NULL, &bufferLength));
|
||||
packetBuffer.assign(bufferData, bufferData + bufferLength);
|
||||
HRC(outputBuffer->Unlock());
|
||||
|
||||
HRC(outputSample->GetSampleTime(&samplePts));
|
||||
|
||||
*pts = samplePts * 100;
|
||||
*data = &packetBuffer[0];
|
||||
*dataLength = bufferLength;
|
||||
*status = SUCCESS;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
*status = FAILURE;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MFAAC::Encoder::ExtraData(UINT8 **extraData_, UINT32 *extraDataLength)
|
||||
{
|
||||
*extraData_ = extraData;
|
||||
*extraDataLength = sizeof(extraData);
|
||||
return true;
|
||||
}
|
91
plugins/win-mf/mf-aac-encoder.hpp
Normal file
91
plugins/win-mf/mf-aac-encoder.hpp
Normal file
@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_MEAN_AND_LEAN
|
||||
#include <Windows.h>
|
||||
#undef WIN32_MEAN_AND_LEAN
|
||||
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include <util/windows/ComPtr.hpp>
|
||||
|
||||
#define MF_LOG(level, format, ...) \
|
||||
blog(level, "[Media Foundation encoder]: " format, ##__VA_ARGS__)
|
||||
#define MF_LOG_ENCODER(format_name, encoder, level, format, ...) \
|
||||
blog(level, "[Media Foundation %s: '%s']: " format, \
|
||||
format_name, obs_encoder_get_name(encoder), \
|
||||
##__VA_ARGS__)
|
||||
|
||||
namespace MFAAC {
|
||||
|
||||
enum Status {
|
||||
FAILURE,
|
||||
SUCCESS,
|
||||
NOT_ACCEPTING,
|
||||
NEED_MORE_INPUT
|
||||
};
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
Encoder(const obs_encoder_t *encoder, UINT32 bitrate, UINT32 channels,
|
||||
UINT32 sampleRate, UINT32 bitsPerSample)
|
||||
: encoder(encoder),
|
||||
bitrate(bitrate),
|
||||
channels(channels),
|
||||
sampleRate(sampleRate),
|
||||
bitsPerSample(bitsPerSample)
|
||||
{}
|
||||
|
||||
Encoder& operator=(Encoder const&) = delete;
|
||||
|
||||
bool Initialize();
|
||||
bool ProcessInput(UINT8 *data, UINT32 dataLength,
|
||||
UINT64 pts, MFAAC::Status *status);
|
||||
bool ProcessOutput(UINT8 **data, UINT32 *dataLength,
|
||||
UINT64 *pts, MFAAC::Status *status);
|
||||
bool ExtraData(UINT8 **extraData, UINT32 *extraDataLength);
|
||||
|
||||
const obs_encoder_t *ObsEncoder() { return encoder; }
|
||||
UINT32 Bitrate() { return bitrate; }
|
||||
UINT32 Channels() { return channels; }
|
||||
UINT32 SampleRate() { return sampleRate; }
|
||||
UINT32 BitsPerSample() { return bitsPerSample; }
|
||||
|
||||
static const UINT32 FrameSize = 1024;
|
||||
|
||||
private:
|
||||
void InitializeExtraData();
|
||||
HRESULT CreateMediaTypes(ComPtr<IMFMediaType> &inputType,
|
||||
ComPtr<IMFMediaType> &outputType);
|
||||
HRESULT EnsureCapacity(ComPtr<IMFSample> &sample, DWORD length);
|
||||
HRESULT CreateEmptySample(ComPtr<IMFSample> &sample,
|
||||
ComPtr<IMFMediaBuffer> &buffer, DWORD length);
|
||||
|
||||
private:
|
||||
const obs_encoder_t *encoder;
|
||||
const UINT32 bitrate;
|
||||
const UINT32 channels;
|
||||
const UINT32 sampleRate;
|
||||
const UINT32 bitsPerSample;
|
||||
|
||||
ComPtr<IMFTransform> transform;
|
||||
ComPtr<IMFSample> outputSample;
|
||||
std::vector<BYTE> packetBuffer;
|
||||
UINT8 extraData[5];
|
||||
};
|
||||
|
||||
static const UINT32 FrameSize = 1024;
|
||||
|
||||
UINT32 FindBestBitrateMatch(UINT32 value);
|
||||
UINT32 FindBestChannelsMatch(UINT32 value);
|
||||
UINT32 FindBestBitsPerSampleMatch(UINT32 value);
|
||||
UINT32 FindBestSamplerateMatch(UINT32 value);
|
||||
bool BitrateValid(UINT32 value);
|
||||
bool ChannelsValid(UINT32 value);
|
||||
bool BitsPerSampleValid(UINT32 value);
|
||||
bool SamplerateValid(UINT32 value);
|
||||
|
||||
}
|
168
plugins/win-mf/mf-aac.cpp
Normal file
168
plugins/win-mf/mf-aac.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
#include <obs-module.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "mf-aac-encoder.hpp"
|
||||
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
using namespace MFAAC;
|
||||
|
||||
static const char *MFAAC_GetName()
|
||||
{
|
||||
return obs_module_text("MFAACEnc");
|
||||
}
|
||||
|
||||
static obs_properties_t *MFAAC_GetProperties(void *)
|
||||
{
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_properties_add_int(props, "bitrate",
|
||||
obs_module_text("Bitrate"), 96, 192, 32);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static void MFAAC_GetDefaults(obs_data_t *settings)
|
||||
{
|
||||
obs_data_set_default_int(settings, "bitrate", 128);
|
||||
}
|
||||
|
||||
static void *MFAAC_Create(obs_data_t *settings, obs_encoder_t *encoder)
|
||||
{
|
||||
UINT32 bitrate = (UINT32)obs_data_get_int(settings, "bitrate");
|
||||
if (!bitrate) {
|
||||
MF_LOG_ENCODER("AAC", encoder, LOG_ERROR,
|
||||
"Invalid bitrate specified");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audio_t *audio = obs_encoder_audio(encoder);
|
||||
UINT32 channels = (UINT32)audio_output_get_channels(audio);
|
||||
UINT32 sampleRate = audio_output_get_sample_rate(audio);
|
||||
UINT32 bitsPerSample = 16;
|
||||
|
||||
UINT32 recommendedSampleRate = FindBestSamplerateMatch(sampleRate);
|
||||
if (recommendedSampleRate != sampleRate) {
|
||||
MF_LOG_ENCODER("AAC", encoder, LOG_WARNING,
|
||||
"unsupported sample rate; "
|
||||
"resampling to best guess '%d' instead of '%d'",
|
||||
recommendedSampleRate, sampleRate);
|
||||
sampleRate = recommendedSampleRate;
|
||||
}
|
||||
|
||||
UINT32 recommendedBitRate = FindBestBitrateMatch(bitrate);
|
||||
if (recommendedBitRate != bitrate) {
|
||||
MF_LOG_ENCODER("AAC", encoder, LOG_WARNING,
|
||||
"unsupported bitrate; "
|
||||
"resampling to best guess '%d' instead of '%d'",
|
||||
recommendedBitRate, bitrate);
|
||||
bitrate = recommendedBitRate;
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> enc(new Encoder(encoder,
|
||||
bitrate, channels, sampleRate, bitsPerSample));
|
||||
|
||||
audio_convert_info aci;
|
||||
aci.samples_per_sec = sampleRate;
|
||||
|
||||
if (!enc->Initialize())
|
||||
return nullptr;
|
||||
|
||||
return enc.release();
|
||||
}
|
||||
|
||||
static void MFAAC_Destroy(void *data)
|
||||
{
|
||||
Encoder *enc = static_cast<Encoder *>(data);
|
||||
delete enc;
|
||||
}
|
||||
|
||||
static bool MFAAC_Encode(void *data, struct encoder_frame *frame,
|
||||
struct encoder_packet *packet, bool *received_packet)
|
||||
{
|
||||
Encoder *enc = static_cast<Encoder *>(data);
|
||||
Status status;
|
||||
|
||||
if (!enc->ProcessInput(frame->data[0], frame->linesize[0], frame->pts,
|
||||
&status))
|
||||
return false;
|
||||
|
||||
// This shouldn't happen since we drain right after
|
||||
// we process input
|
||||
if (status == NOT_ACCEPTING)
|
||||
return false;
|
||||
|
||||
UINT8 *outputData;
|
||||
UINT32 outputDataLength;
|
||||
UINT64 outputPts;
|
||||
|
||||
if (!enc->ProcessOutput(&outputData, &outputDataLength, &outputPts,
|
||||
&status))
|
||||
return false;
|
||||
|
||||
// Needs more input, not a failure case
|
||||
if (status == NEED_MORE_INPUT)
|
||||
return true;
|
||||
|
||||
packet->pts = outputPts;
|
||||
packet->dts = outputPts;
|
||||
packet->data = outputData;
|
||||
packet->size = outputDataLength;
|
||||
packet->type = OBS_ENCODER_AUDIO;
|
||||
packet->timebase_num = 1;
|
||||
packet->timebase_den = enc->SampleRate();
|
||||
|
||||
return *received_packet = true;
|
||||
}
|
||||
|
||||
static bool MFAAC_GetExtraData(void *data, uint8_t **extra_data, size_t *size)
|
||||
{
|
||||
Encoder *enc = static_cast<Encoder *>(data);
|
||||
|
||||
UINT32 length;
|
||||
if (enc->ExtraData(extra_data, &length)) {
|
||||
*size = length;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void MFAAC_GetAudioInfo(void *, struct audio_convert_info *info)
|
||||
{
|
||||
info->format = AUDIO_FORMAT_16BIT;
|
||||
info->samples_per_sec = FindBestSamplerateMatch(info->samples_per_sec);
|
||||
}
|
||||
|
||||
static size_t MFAAC_GetFrameSize(void *)
|
||||
{
|
||||
return Encoder::FrameSize;
|
||||
}
|
||||
|
||||
extern "C" void RegisterMFAACEncoder()
|
||||
{
|
||||
if (!IsWindows8OrGreater()) {
|
||||
MF_LOG(LOG_WARNING, "plugin is disabled for performance "
|
||||
"reasons on Windows versions less than 8");
|
||||
return;
|
||||
}
|
||||
|
||||
obs_encoder_info info = {};
|
||||
info.id = "mf_aac";
|
||||
info.type = OBS_ENCODER_AUDIO;
|
||||
info.codec = "AAC";
|
||||
info.get_name = MFAAC_GetName;
|
||||
info.create = MFAAC_Create;
|
||||
info.destroy = MFAAC_Destroy;
|
||||
info.encode = MFAAC_Encode;
|
||||
info.get_frame_size = MFAAC_GetFrameSize;
|
||||
info.get_defaults = MFAAC_GetDefaults;
|
||||
info.get_properties = MFAAC_GetProperties;
|
||||
info.get_extra_data = MFAAC_GetExtraData;
|
||||
info.get_audio_info = MFAAC_GetAudioInfo;
|
||||
|
||||
MF_LOG(LOG_INFO, "Adding Media Foundation AAC Encoder");
|
||||
|
||||
obs_register_encoder(&info);
|
||||
|
||||
}
|
12
plugins/win-mf/mf-plugin.c
Normal file
12
plugins/win-mf/mf-plugin.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <obs-module.h>
|
||||
|
||||
extern void RegisterMFAACEncoder();
|
||||
|
||||
bool obs_module_load(void)
|
||||
{
|
||||
RegisterMFAACEncoder();
|
||||
return true;
|
||||
}
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
OBS_MODULE_USE_DEFAULT_LOCALE("win-mf", "en-US")
|
Loading…
Reference in New Issue
Block a user