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+ " \t bitrate: %d\n "
205+ " \t channels: %d\n "
206+ " \t sample rate: %d\n "
207+ " \t bits-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 (¤tLength));
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