| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- #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;
- }
|