Skip to content

Commit 02814ef

Browse files
thirumalai-qcomthirumalai-qcom
authored andcommitted
win-mf: Add Media Foundation AAC encoder for WoA
This commit introduces AAC encoder support using the Media Foundation Transform (MFT) interface, specifically targeting Windows on ARM (WoA) devices with Qualcomm hardware. It adds mf-aac.cpp and mf-aac-encoder.cpp/.hpp to implement the encoding logic. Additionally, CMakeLists.txt is updated to include the new source and header files for proper integration into the win-mf plugin
1 parent e0982d9 commit 02814ef

File tree

4 files changed

+625
-1
lines changed

4 files changed

+625
-1
lines changed

plugins/win-mf/CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@ set(
1111
mf-h264-encoder.cpp
1212
mf-hevc.cpp
1313
mf-hevc-encoder.cpp
14+
mf-aac.cpp
15+
mf-aac-encoder.cpp
1416
)
1517

16-
set(win-mf_HEADERS mf-common.hpp mf-encoder-descriptor.hpp mf-h264-encoder.hpp mf-hevc-encoder.hpp)
18+
set(
19+
win-mf_HEADERS
20+
mf-common.hpp
21+
mf-encoder-descriptor.hpp
22+
mf-h264-encoder.hpp
23+
mf-hevc-encoder.hpp
24+
mf-aac-encoder.hpp
25+
)
1726

1827
add_library(win-mf MODULE)
1928

plugins/win-mf/mf-aac-encoder.cpp

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
#include <obs-module.h>
2+
3+
#include "mf-aac-encoder.hpp"
4+
5+
#include <mferror.h>
6+
#include <mftransform.h>
7+
#include <wmcodecdsp.h>
8+
#include <comdef.h>
9+
10+
#include <array>
11+
12+
inline void LogAAC(const obs_encoder_t *encoder, int level, const char *format, ...)
13+
{
14+
va_list args;
15+
va_start(args, format);
16+
17+
char formattedMessage[1024];
18+
vsnprintf(formattedMessage, sizeof(formattedMessage), format, args);
19+
va_end(args);
20+
21+
blog(level, "[Media Foundation AAC: '%s']: %s", obs_encoder_get_name(encoder), formattedMessage);
22+
}
23+
24+
inline void LogCOMError(const obs_encoder_t *encoder, const char *operation, HRESULT hr)
25+
{
26+
_com_error err(hr);
27+
LogAAC(encoder, LOG_ERROR, "%s failed, %S (0x%08lx)", operation, err.ErrorMessage(), hr);
28+
}
29+
30+
#define HRC(r) \
31+
if (FAILED(hr = (r))) { \
32+
LogCOMError(ObsEncoder(),#r, hr); \
33+
goto fail; \
34+
}
35+
36+
using namespace MFAAC;
37+
38+
template<std::size_t N> constexpr std::array<UINT32, N> MakeConstArray(const UINT32 (&values)[N])
39+
{
40+
std::array<UINT32, N> arr{};
41+
for (std::size_t i = 0; i < N; ++i) {
42+
arr[i] = values[i];
43+
}
44+
return arr;
45+
}
46+
47+
constexpr auto VALID_BITRATES = MakeConstArray({96, 128, 160, 192});
48+
constexpr auto VALID_CHANNELS = MakeConstArray({1, 2});
49+
constexpr auto VALID_BITS_PER_SAMPLE = MakeConstArray({16});
50+
constexpr auto VALID_SAMPLERATES = MakeConstArray({44100, 48000});
51+
52+
template<std::size_t N> constexpr UINT32 FindBestMatch(const std::array<UINT32, N> &validValues, UINT32 value)
53+
{
54+
for (UINT32 val : validValues) {
55+
if (val >= value)
56+
return val;
57+
}
58+
59+
// Only downgrade if no values are better
60+
return validValues[N - 1];
61+
}
62+
63+
template<std::size_t N> static bool IsValid(const std::array<UINT32, N> &validValues, UINT32 value)
64+
{
65+
for (UINT32 val : validValues) {
66+
if (val == value)
67+
return true;
68+
}
69+
70+
return false;
71+
}
72+
73+
UINT32 MFAAC::FindBestBitrateMatch(UINT32 value)
74+
{
75+
return FindBestMatch(VALID_BITRATES, value);
76+
}
77+
78+
UINT32 MFAAC::FindBestChannelsMatch(UINT32 value)
79+
{
80+
return FindBestMatch(VALID_CHANNELS, value);
81+
}
82+
83+
UINT32 MFAAC::FindBestBitsPerSampleMatch(UINT32 value)
84+
{
85+
return FindBestMatch(VALID_BITS_PER_SAMPLE, value);
86+
}
87+
88+
UINT32 MFAAC::FindBestSamplerateMatch(UINT32 value)
89+
{
90+
return FindBestMatch(VALID_SAMPLERATES, value);
91+
}
92+
93+
bool MFAAC::BitrateValid(UINT32 value)
94+
{
95+
return IsValid(VALID_BITRATES, value);
96+
}
97+
98+
bool MFAAC::ChannelsValid(UINT32 value)
99+
{
100+
return IsValid(VALID_CHANNELS, value);
101+
}
102+
103+
bool MFAAC::BitsPerSampleValid(UINT32 value)
104+
{
105+
return IsValid(VALID_BITS_PER_SAMPLE, value);
106+
}
107+
108+
bool MFAAC::SamplerateValid(UINT32 value)
109+
{
110+
return IsValid(VALID_SAMPLERATES, value);
111+
}
112+
113+
HRESULT MFAAC::Encoder::CreateMediaTypes(ComPtr<IMFMediaType> &i, ComPtr<IMFMediaType> &o)
114+
{
115+
HRESULT hr;
116+
HRC(MFCreateMediaType(&i));
117+
HRC(MFCreateMediaType(&o));
118+
119+
HRC(i->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
120+
HRC(i->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
121+
HRC(i->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample));
122+
HRC(i->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate));
123+
HRC(i->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels));
124+
125+
HRC(o->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
126+
HRC(o->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC));
127+
HRC(o->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample));
128+
HRC(o->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate));
129+
HRC(o->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels));
130+
HRC(o->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, (bitrate * 1000) / 8));
131+
132+
return S_OK;
133+
fail:
134+
return hr;
135+
}
136+
137+
constexpr uint16_t SWAPU16(uint16_t x)
138+
{
139+
return (x >> 8) | (x << 8);
140+
}
141+
142+
void MFAAC::Encoder::InitializeExtraData()
143+
{
144+
UINT16 *extraData16 = (UINT16 *)extraData;
145+
UINT16 profile = 2; //Low Complexity
146+
// Profile
147+
// XXXX X... .... ....
148+
*extraData16 = profile << 11;
149+
// Sample Index (3=48, 4=44.1)
150+
// .... .XXX X... ....
151+
*extraData16 |= (sampleRate == 48000 ? 3 : 4) << 7;
152+
// Channels
153+
// .... .... .XXX X...
154+
*extraData16 |= channels << 3;
155+
*extraData16 = SWAPU16(*extraData16);
156+
157+
// Extensions
158+
extraData16++;
159+
*extraData16 = 0x2b7 << 5;
160+
// Profile
161+
*extraData16 |= profile;
162+
*extraData16 = SWAPU16(*extraData16);
163+
164+
extraData[4] = 0;
165+
}
166+
167+
bool MFAAC::Encoder::Initialize()
168+
{
169+
HRESULT hr;
170+
171+
ComPtr<IMFTransform> transform_;
172+
ComPtr<IMFMediaType> inputType, outputType;
173+
174+
if (!BitrateValid(bitrate)) {
175+
LogAAC(ObsEncoder(), LOG_WARNING, "invalid bitrate (kbps) '%d'", bitrate);
176+
return false;
177+
}
178+
if (!ChannelsValid(channels)) {
179+
LogAAC(ObsEncoder(), LOG_WARNING, "invalid channel count '%d", channels);
180+
return false;
181+
}
182+
if (!SamplerateValid(sampleRate)) {
183+
LogAAC(ObsEncoder(), LOG_WARNING, "invalid sample rate (hz) '%d'", sampleRate);
184+
return false;
185+
}
186+
if (!BitsPerSampleValid(bitsPerSample)) {
187+
LogAAC(ObsEncoder(), LOG_WARNING, "invalid bits-per-sample (bits) '%d'", bitsPerSample);
188+
return false;
189+
}
190+
191+
InitializeExtraData();
192+
193+
HRC(CoCreateInstance(CLSID_AACMFTEncoder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&transform_)));
194+
HRC(CreateMediaTypes(inputType, outputType));
195+
196+
HRC(transform_->SetInputType(0, inputType.Get(), 0));
197+
HRC(transform_->SetOutputType(0, outputType.Get(), 0));
198+
199+
HRC(transform_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL));
200+
HRC(transform_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL));
201+
202+
LogAAC(ObsEncoder(), LOG_INFO,
203+
"encoder created\n"
204+
"\tbitrate: %d\n"
205+
"\tchannels: %d\n"
206+
"\tsample rate: %d\n"
207+
"\tbits-per-sample: %d\n",
208+
bitrate, channels, sampleRate, bitsPerSample);
209+
210+
transform = transform_;
211+
return true;
212+
213+
fail:
214+
return false;
215+
}
216+
217+
HRESULT MFAAC::Encoder::CreateEmptySample(ComPtr<IMFSample> &sample, ComPtr<IMFMediaBuffer> &buffer, DWORD length)
218+
{
219+
HRESULT hr;
220+
221+
HRC(MFCreateSample(&sample));
222+
HRC(MFCreateMemoryBuffer(length, &buffer));
223+
HRC(sample->AddBuffer(buffer.Get()));
224+
return S_OK;
225+
226+
fail:
227+
return hr;
228+
}
229+
230+
HRESULT MFAAC::Encoder::EnsureCapacity(ComPtr<IMFSample> &sample, DWORD length)
231+
{
232+
HRESULT hr;
233+
ComPtr<IMFMediaBuffer> buffer;
234+
DWORD currentLength;
235+
236+
if (!sample) {
237+
HRC(CreateEmptySample(sample, buffer, length));
238+
} else {
239+
HRC(sample->GetBufferByIndex(0, &buffer));
240+
}
241+
242+
HRC(buffer->GetMaxLength(&currentLength));
243+
if (currentLength < length) {
244+
HRC(sample->RemoveAllBuffers());
245+
HRC(MFCreateMemoryBuffer(length, &buffer));
246+
HRC(sample->AddBuffer(buffer));
247+
} else {
248+
buffer->SetCurrentLength(0);
249+
}
250+
251+
packetBuffer.reserve(length);
252+
253+
return S_OK;
254+
255+
fail:
256+
return hr;
257+
}
258+
259+
bool MFAAC::Encoder::ProcessInput(UINT8 *data, UINT32 data_length, UINT64 pts, Status *status)
260+
{
261+
HRESULT hr;
262+
ComPtr<IMFSample> sample;
263+
ComPtr<IMFMediaBuffer> buffer;
264+
BYTE *bufferData;
265+
INT64 samplePts;
266+
UINT32 samples;
267+
UINT64 sampleDur;
268+
269+
HRC(CreateEmptySample(sample, buffer, data_length));
270+
271+
HRC(buffer->Lock(&bufferData, NULL, NULL));
272+
memcpy(bufferData, data, data_length);
273+
HRC(buffer->Unlock());
274+
HRC(buffer->SetCurrentLength(data_length));
275+
276+
samples = data_length / channels / (bitsPerSample / 8);
277+
sampleDur = (UINT64)(((float)sampleRate / channels / samples) * 10000);
278+
samplePts = pts / 100;
279+
280+
HRC(sample->SetSampleTime(samplePts));
281+
HRC(sample->SetSampleDuration(sampleDur));
282+
283+
hr = transform->ProcessInput(0, sample, 0);
284+
if (hr == MF_E_NOTACCEPTING) {
285+
*status = NOT_ACCEPTING;
286+
return true;
287+
} else if (FAILED(hr)) {
288+
LogCOMError(ObsEncoder(), "process input", hr);
289+
return false;
290+
}
291+
292+
*status = SUCCESS;
293+
return true;
294+
295+
fail:
296+
*status = FAILURE;
297+
return false;
298+
}
299+
300+
bool MFAAC::Encoder::ProcessOutput(UINT8 **data, UINT32 *dataLength, UINT64 *pts, Status *status)
301+
{
302+
HRESULT hr;
303+
304+
DWORD outputFlags, outputStatus;
305+
MFT_OUTPUT_STREAM_INFO outputInfo = {0};
306+
MFT_OUTPUT_DATA_BUFFER output = {0};
307+
ComPtr<IMFMediaBuffer> outputBuffer;
308+
BYTE *bufferData;
309+
DWORD bufferLength;
310+
INT64 samplePts;
311+
312+
HRC(transform->GetOutputStatus(&outputFlags));
313+
if (outputFlags != MFT_OUTPUT_STATUS_SAMPLE_READY) {
314+
*status = NEED_MORE_INPUT;
315+
return true;
316+
}
317+
318+
HRC(transform->GetOutputStreamInfo(0, &outputInfo));
319+
EnsureCapacity(outputSample, outputInfo.cbSize);
320+
321+
output.pSample = outputSample.Get();
322+
323+
hr = transform->ProcessOutput(0, 1, &output, &outputStatus);
324+
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
325+
*status = NEED_MORE_INPUT;
326+
return true;
327+
} else if (FAILED(hr)) {
328+
LogCOMError(ObsEncoder(), "process input", hr);
329+
return false;
330+
}
331+
332+
HRC(outputSample->GetBufferByIndex(0, &outputBuffer));
333+
334+
HRC(outputBuffer->Lock(&bufferData, NULL, &bufferLength));
335+
packetBuffer.assign(bufferData, bufferData + bufferLength);
336+
HRC(outputBuffer->Unlock());
337+
338+
HRC(outputSample->GetSampleTime(&samplePts));
339+
340+
*pts = samplePts * 100;
341+
*data = &packetBuffer[0];
342+
*dataLength = bufferLength;
343+
*status = SUCCESS;
344+
return true;
345+
346+
fail:
347+
*status = FAILURE;
348+
return false;
349+
}
350+
351+
bool MFAAC::Encoder::ExtraData(UINT8 **extraData_, UINT32 *extraDataLength)
352+
{
353+
*extraData_ = extraData;
354+
*extraDataLength = sizeof(extraData);
355+
return true;
356+
}

0 commit comments

Comments
 (0)