20#include <freerdp/config.h>
22#include "dsp_mediacodec.h"
24#include <winpr/assert.h>
25#include <winpr/cast.h>
26#include <freerdp/log.h>
28#include <media/NdkMediaCodec.h>
29#include <media/NdkMediaFormat.h>
31#define TAG FREERDP_TAG("dsp.mediacodec")
33static const char* AAC_MIME =
"audio/mp4a-latm";
34static const int AAC_OBJECT_LC = 2;
37static const char* MEDIACODEC_KEY_CSD_0 =
"csd-0";
39static const int64_t MEDIACODEC_DSP_DEQUEUE_TIMEOUT = 10000;
40static const int MEDIACODEC_DSP_MAX_RETRIES = 100;
41static const size_t AAC_FRAME_SAMPLES = 1024;
42static const int32_t DECODER_MAX_INPUT_SIZE = 65536;
43static const UINT32 AAC_MAX_SAMPLE_RATE = 96000;
56 size_t encBufferedBytes;
62 BOOL formatMismatchLogged;
63 BOOL srcMismatchLogged;
66#define mediacodec_aac_warn(status, what) \
67 mediacodec_aac_warn_((status), (what), __FILE__, __func__, __LINE__)
69static void mediacodec_aac_warn_(media_status_t status,
const char* what,
const char* file,
70 const char* fkt,
size_t line)
72 if (status == AMEDIA_OK)
75 wLog* log = WLog_Get(TAG);
76 if (WLog_IsLevelActive(log, WLOG_WARN))
77 WLog_PrintTextMessage(log, WLOG_WARN, line, file, fkt,
"%s failed: %d", what, status);
82static int aac_sample_rate_index(UINT32 rate)
84 static const UINT32 rates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000,
85 22050, 16000, 12000, 11025, 8000, 7350 };
86 for (
size_t i = 0; i < ARRAYSIZE(rates); i++)
89 return WINPR_ASSERTING_INT_CAST(
int, i);
95static BOOL aac_build_asc(UINT32 rate, UINT16 channels, BYTE asc[2])
99 const int sfIndex = aac_sample_rate_index(rate);
103 if ((channels < 1) || (channels > 8))
107 const int channelConfig = (channels == 8) ? 7 : channels;
109 asc[0] = WINPR_ASSERTING_INT_CAST(BYTE, (AAC_OBJECT_LC << 3) | ((sfIndex >> 1) & 0x07));
111 WINPR_ASSERTING_INT_CAST(BYTE, ((sfIndex & 0x01) << 7) | ((channelConfig & 0x0F) << 3));
117static BOOL aac_asc_plausible(
const BYTE* data,
size_t size)
124 const int objectType = (data[0] >> 3) & 0x1F;
125 const int sfIndex = ((data[0] & 0x07) << 1) | ((data[1] >> 7) & 0x01);
142 if (((objectType == 5) || (objectType == 29)) && (size < 4))
152 return (((data[1] >> 3) & 0x0F) <= 7);
159static BOOL aac_format_asc(
const AUDIO_FORMAT* format,
const BYTE** pasc,
size_t* psize)
161 WINPR_ASSERT(format);
165 const size_t heaacInfoSize = 12;
167 if (!format->data || (format->cbSize < 2))
170 if (aac_asc_plausible(format->data, format->cbSize))
172 *pasc = format->data;
173 *psize = format->cbSize;
177 if ((format->cbSize >= heaacInfoSize + 2) &&
178 aac_asc_plausible(&format->data[heaacInfoSize], format->cbSize - heaacInfoSize))
180 *pasc = &format->data[heaacInfoSize];
181 *psize = format->cbSize - heaacInfoSize;
189static BOOL mediacodec_drain_output(MEDIACODEC_AAC* mc,
wStream* out)
196 int64_t timeout = MEDIACODEC_DSP_DEQUEUE_TIMEOUT;
198 for (
int loops = 0; loops <= MEDIACODEC_DSP_MAX_RETRIES; loops++)
200 AMediaCodecBufferInfo info = { 0 };
201 const ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mc->codec, &info, timeout);
206 if (info.size > 0 && (info.flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0)
209 uint8_t* outBuf = AMediaCodec_getOutputBuffer(mc->codec, (
size_t)outIdx, &outCap);
212 if (outBuf && ((info.offset < 0) || ((
size_t)info.offset > outCap) ||
213 ((
size_t)info.size > outCap - (
size_t)info.offset)))
215 WLog_ERR(TAG,
"output buffer info out of range: offset %d size %d cap %zu",
216 info.offset, info.size, outCap);
218 AMediaCodec_releaseOutputBuffer(mc->codec, (
size_t)outIdx, FALSE),
219 "releaseOutputBuffer");
225 if (!Stream_EnsureRemainingCapacity(out, (
size_t)info.size))
228 AMediaCodec_releaseOutputBuffer(mc->codec, (
size_t)outIdx, FALSE),
229 "releaseOutputBuffer");
232 Stream_Write(out, outBuf + info.offset, (
size_t)info.size);
235 const media_status_t rc =
236 AMediaCodec_releaseOutputBuffer(mc->codec, (
size_t)outIdx, FALSE);
239 WLog_ERR(TAG,
"releaseOutputBuffer failed: %d", rc);
243 else if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
245 AMediaFormat* newFormat = AMediaCodec_getOutputFormat(mc->codec);
250 if (AMediaFormat_getInt32(newFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, &newRate) &&
252 mc->outSampleRate = (UINT32)newRate;
253 if (AMediaFormat_getInt32(newFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &newCh) &&
254 (newCh > 0) && (newCh <= UINT16_MAX))
255 mc->outChannels = WINPR_ASSERTING_INT_CAST(UINT16, newCh);
256 mediacodec_aac_warn(AMediaFormat_delete(newFormat),
"AMediaFormat_delete");
258 WLog_DBG(TAG,
"output format %" PRIu32
" Hz, %" PRIu16
" ch", mc->outSampleRate,
264 if (!mc->formatMismatchLogged &&
265 ((mc->outSampleRate != mc->sampleRate) || (mc->outChannels != mc->channels)))
267 mc->formatMismatchLogged = TRUE;
269 "decoder output %" PRIu32
" Hz/%" PRIu16
" ch differs from the "
270 "negotiated %" PRIu32
" Hz/%" PRIu16
" ch, playback will be wrong",
271 mc->outSampleRate, mc->outChannels, mc->sampleRate, mc->channels);
275 else if (outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)
279 else if (outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
285 WLog_ERR(TAG,
"dequeueOutputBuffer returned %zd", outIdx);
293static BOOL mediacodec_feed_buffer(MEDIACODEC_AAC* mc,
const BYTE* data,
size_t length,
294 size_t samplesQueued,
wStream* out)
302 while ((inIdx = AMediaCodec_dequeueInputBuffer(mc->codec, MEDIACODEC_DSP_DEQUEUE_TIMEOUT)) < 0)
304 if (inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER)
306 WLog_ERR(TAG,
"dequeueInputBuffer failed [%zd]", inIdx);
310 if (++retries > MEDIACODEC_DSP_MAX_RETRIES)
312 WLog_ERR(TAG,
"dequeueInputBuffer timeout [%zd]", inIdx);
318 uint8_t* inBuf = AMediaCodec_getInputBuffer(mc->codec, (
size_t)inIdx, &inCap);
319 if (!inBuf || inCap < length)
321 WLog_ERR(TAG,
"input buffer too small: inCap %zu < length %zu", inCap, length);
322 mediacodec_aac_warn(AMediaCodec_queueInputBuffer(mc->codec, (
size_t)inIdx, 0, 0, 0, 0),
327 memcpy(inBuf, data, length);
331 if (mc->sampleRate > 0)
332 ptsUs = (mc->sampleCount / mc->sampleRate) * 1000000ULL +
333 (mc->sampleCount % mc->sampleRate) * 1000000ULL / mc->sampleRate;
334 mc->sampleCount += samplesQueued;
336 if (AMediaCodec_queueInputBuffer(mc->codec, (
size_t)inIdx, 0, length, ptsUs, 0) != AMEDIA_OK)
338 WLog_ERR(TAG,
"queueInputBuffer failed");
342 return mediacodec_drain_output(mc, out);
347 WINPR_ASSERT(context);
348 WINPR_UNUSED(frames_per_packet);
350 MEDIACODEC_AAC* mc = (MEDIACODEC_AAC*)calloc(1,
sizeof(MEDIACODEC_AAC));
353 context->mediacodecAacInstance = mc;
355 if ((context->format.nSamplesPerSec == 0) ||
356 (context->format.nSamplesPerSec > AAC_MAX_SAMPLE_RATE) || (context->format.nChannels < 1) ||
357 (context->format.nChannels > 8))
359 WLog_ERR(TAG,
"unusable AAC format %" PRIu32
" Hz, %" PRIu16
" ch",
360 context->format.nSamplesPerSec, context->format.nChannels);
362 context->mediacodecAacInstance =
nullptr;
366 mc->isEncoder = context->encoder;
367 mc->sampleRate = context->format.nSamplesPerSec;
368 mc->channels = context->format.nChannels;
369 mc->outSampleRate = mc->sampleRate;
370 mc->outChannels = mc->channels;
375 mc->encFrameBytes = AAC_FRAME_SAMPLES * mc->channels *
sizeof(int16_t);
376 mc->encBuffer = (BYTE*)calloc(mc->encFrameBytes,
sizeof(BYTE));
380 context->mediacodecAacInstance =
nullptr;
383 mc->encBufferedBytes = 0;
386 mc->codec = context->encoder ? AMediaCodec_createEncoderByType(AAC_MIME)
387 : AMediaCodec_createDecoderByType(AAC_MIME);
390 WLog_ERR(TAG,
"AMediaCodec_create%scoderByType failed", context->encoder ?
"En" :
"De");
393 context->mediacodecAacInstance =
nullptr;
397 AMediaFormat* format = AMediaFormat_new();
400 mediacodec_aac_dsp_uninit(context);
404 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, AAC_MIME);
405 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE,
406 WINPR_ASSERTING_INT_CAST(int32_t, mc->sampleRate));
407 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT,
408 WINPR_ASSERTING_INT_CAST(int32_t, mc->channels));
410 if (context->encoder)
412 const UINT64 bps = (UINT64)context->format.nAvgBytesPerSec * 8;
413 int32_t bitrate = 128000;
417 bitrate = WINPR_ASSERTING_INT_CAST(int32_t, bps);
418 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, bitrate);
419 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_AAC_PROFILE, AAC_OBJECT_LC);
424 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, DECODER_MAX_INPUT_SIZE);
426 const BYTE* asc =
nullptr;
430 if (!aac_format_asc(&context->format, &asc, &ascSize))
432 if (!aac_build_asc(mc->sampleRate, mc->channels, built))
434 WLog_ERR(TAG,
"unsupported AAC sample rate %" PRIu32, mc->sampleRate);
435 mediacodec_aac_warn(AMediaFormat_delete(format),
"AMediaFormat_delete");
436 mediacodec_aac_dsp_uninit(context);
441 ascSize =
sizeof(built);
444 AMediaFormat_setBuffer(format, MEDIACODEC_KEY_CSD_0, asc, ascSize);
447 media_status_t status =
448 AMediaCodec_configure(mc->codec, format,
nullptr,
nullptr,
449 context->encoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0);
450 mediacodec_aac_warn(AMediaFormat_delete(format),
"AMediaFormat_delete");
451 if (status != AMEDIA_OK)
453 WLog_ERR(TAG,
"AMediaCodec_configure failed: %d", status);
454 mediacodec_aac_dsp_uninit(context);
458 status = AMediaCodec_start(mc->codec);
459 if (status != AMEDIA_OK)
461 WLog_ERR(TAG,
"AMediaCodec_start failed: %d", status);
462 mediacodec_aac_dsp_uninit(context);
467 WLog_DBG(TAG,
"MediaCodec AAC %scoder started: %" PRIu32
" Hz, %" PRIu16
" ch",
468 context->encoder ?
"en" :
"de", mc->sampleRate, mc->channels);
477 MEDIACODEC_AAC* mc = (MEDIACODEC_AAC*)context->mediacodecAacInstance;
484 mediacodec_aac_warn(AMediaCodec_stop(mc->codec),
"AMediaCodec_stop");
485 mediacodec_aac_warn(AMediaCodec_delete(mc->codec),
"AMediaCodec_delete");
489 context->mediacodecAacInstance =
nullptr;
493 const BYTE* data,
size_t length,
wStream* out)
495 WINPR_ASSERT(context);
496 WINPR_ASSERT(srcFormat);
498 if (srcFormat->wFormatTag != WAVE_FORMAT_PCM)
500 WLog_WARN(TAG,
"expected PCM input, got 0x%04x", (
unsigned)srcFormat->wFormatTag);
504 MEDIACODEC_AAC* mc = (MEDIACODEC_AAC*)context->mediacodecAacInstance;
505 if (!mc || !mc->codec || !mc->started || !data || length == 0 || !out)
508 if (srcFormat->wBitsPerSample != 16)
510 WLog_ERR(TAG,
"expected 16 bit PCM, got %" PRIu16
" bits", srcFormat->wBitsPerSample);
514 const size_t srcFrameBytes = srcFormat->nChannels *
sizeof(int16_t);
515 if ((srcFrameBytes == 0) || ((length % srcFrameBytes) != 0))
517 WLog_ERR(TAG,
"PCM length %" PRIuz
" is not a multiple of the %" PRIuz
" byte frame",
518 length, srcFrameBytes);
525 if (!mc->srcMismatchLogged &&
526 ((srcFormat->nSamplesPerSec != mc->sampleRate) || (srcFormat->nChannels != mc->channels)))
528 mc->srcMismatchLogged = TRUE;
530 "PCM source %" PRIu32
" Hz/%" PRIu16
" ch differs from the negotiated %" PRIu32
531 " Hz/%" PRIu16
" ch, encoded audio will be wrong",
532 srcFormat->nSamplesPerSec, srcFormat->nChannels, mc->sampleRate, mc->channels);
535 const BYTE* inPtr = data;
536 size_t inRemaining = length;
538 while (inRemaining > 0)
541 const size_t needed = mc->encFrameBytes - mc->encBufferedBytes;
542 const size_t toCopy = (inRemaining < needed) ? inRemaining : needed;
544 memcpy(mc->encBuffer + mc->encBufferedBytes, inPtr, toCopy);
545 mc->encBufferedBytes += toCopy;
547 inRemaining -= toCopy;
549 if (mc->encBufferedBytes == mc->encFrameBytes)
553 const BOOL fed = mediacodec_feed_buffer(mc, mc->encBuffer, mc->encFrameBytes,
554 AAC_FRAME_SAMPLES, out);
555 mc->encBufferedBytes = 0;
565 const BYTE* data,
size_t length,
wStream* out)
567 WINPR_ASSERT(context);
568 WINPR_ASSERT(srcFormat);
570 if (srcFormat->wFormatTag != WAVE_FORMAT_AAC_MS)
572 WLog_WARN(TAG,
"expected AAC input, got 0x%04x", (
unsigned)srcFormat->wFormatTag);
576 MEDIACODEC_AAC* mc = (MEDIACODEC_AAC*)context->mediacodecAacInstance;
577 if (!mc || !mc->codec || !mc->started || !data || length == 0 || !out)
581 return mediacodec_feed_buffer(mc, data, length, AAC_FRAME_SAMPLES, out);
584BOOL mediacodec_aac_dsp_supports_format(
const AUDIO_FORMAT* format, BOOL encode)
586 WINPR_ASSERT(format);
588 if (format->wFormatTag != WAVE_FORMAT_AAC_MS)
591 if ((format->nSamplesPerSec == 0) || (format->nSamplesPerSec > AAC_MAX_SAMPLE_RATE) ||
592 (format->nChannels < 1) || (format->nChannels > 8))
595 if (encode && (format->nChannels > 2))
600 const BYTE* asc =
nullptr;
603 if (aac_format_asc(format, &asc, &ascSize))
609 return aac_sample_rate_index(format->nSamplesPerSec) >= 0;