20#include <winpr/wlog.h>
21#include <winpr/assert.h>
22#include <winpr/library.h>
24#include <freerdp/log.h>
25#include <freerdp/codec/h264.h>
27#include <media/NdkMediaCodec.h>
28#include <media/NdkMediaFormat.h>
32static const char* CODEC_NAME =
"video/avc";
34static const int COLOR_FormatYUV420Planar = 19;
35static const int COLOR_FormatYUV420Flexible = 0x7f420888;
38static const int MEDIACODEC_MINIMUM_WIDTH = 320;
39static const int MEDIACODEC_MINIMUM_HEIGHT = 240;
44 AMediaFormat* inputFormat;
45 AMediaFormat* outputFormat;
50 ssize_t currentOutputBufferIndex;
51} H264_CONTEXT_MEDIACODEC;
53static AMediaFormat* mediacodec_format_new(wLog* log,
int width,
int height)
55 const char* media_format;
56 AMediaFormat* format = AMediaFormat_new();
57 if (format ==
nullptr)
59 WLog_Print(log, WLOG_ERROR,
"AMediaFormat_new failed");
63 AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, CODEC_NAME);
64 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, width);
65 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height);
66 AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Planar);
68 media_format = AMediaFormat_toString(format);
69 if (media_format ==
nullptr)
71 WLog_Print(log, WLOG_ERROR,
"AMediaFormat_toString failed");
72 AMediaFormat_delete(format);
76 WLog_Print(log, WLOG_DEBUG,
"MediaCodec configuring with desired output format [%s]",
82static void set_mediacodec_format(H264_CONTEXT* h264, AMediaFormat** formatVariable,
83 AMediaFormat* newFormat)
85 media_status_t status = AMEDIA_OK;
86 H264_CONTEXT_MEDIACODEC* sys;
89 WINPR_ASSERT(formatVariable);
91 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
94 if (*formatVariable == newFormat)
97 if (*formatVariable !=
nullptr)
99 status = AMediaFormat_delete(*formatVariable);
100 if (status != AMEDIA_OK)
102 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaFormat_delete %d", status);
106 *formatVariable = newFormat;
109static int update_mediacodec_inputformat(H264_CONTEXT* h264)
111 H264_CONTEXT_MEDIACODEC* sys;
112 AMediaFormat* inputFormat;
113 const char* mediaFormatName;
117 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
121 inputFormat = AMediaCodec_getInputFormat(sys->decoder);
122 if (inputFormat ==
nullptr)
124 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_getInputFormat failed");
128 inputFormat = sys->inputFormat;
130 set_mediacodec_format(h264, &sys->inputFormat, inputFormat);
132 mediaFormatName = AMediaFormat_toString(sys->inputFormat);
133 if (mediaFormatName ==
nullptr)
135 WLog_Print(h264->log, WLOG_ERROR,
"AMediaFormat_toString failed");
138 WLog_Print(h264->log, WLOG_DEBUG,
"Using MediaCodec with input MediaFormat [%s]",
144static int update_mediacodec_outputformat(H264_CONTEXT* h264)
146 H264_CONTEXT_MEDIACODEC* sys;
147 AMediaFormat* outputFormat;
148 const char* mediaFormatName;
149 int32_t outputWidth, outputHeight;
153 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
156 outputFormat = AMediaCodec_getOutputFormat(sys->decoder);
157 if (outputFormat ==
nullptr)
159 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_getOutputFormat failed");
162 set_mediacodec_format(h264, &sys->outputFormat, outputFormat);
164 mediaFormatName = AMediaFormat_toString(sys->outputFormat);
165 if (mediaFormatName ==
nullptr)
167 WLog_Print(h264->log, WLOG_ERROR,
"AMediaFormat_toString failed");
170 WLog_Print(h264->log, WLOG_DEBUG,
"Using MediaCodec with output MediaFormat [%s]",
173 if (!AMediaFormat_getInt32(sys->outputFormat, AMEDIAFORMAT_KEY_WIDTH, &outputWidth))
175 WLog_Print(h264->log, WLOG_ERROR,
"fnAMediaFormat_getInt32 failed getting width");
179 if (!AMediaFormat_getInt32(sys->outputFormat, AMEDIAFORMAT_KEY_HEIGHT, &outputHeight))
181 WLog_Print(h264->log, WLOG_ERROR,
"fnAMediaFormat_getInt32 failed getting height");
185 sys->outputWidth = outputWidth;
186 sys->outputHeight = outputHeight;
191static void release_current_outputbuffer(H264_CONTEXT* h264)
193 media_status_t status = AMEDIA_OK;
194 H264_CONTEXT_MEDIACODEC* sys;
197 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
200 if (sys->currentOutputBufferIndex < 0)
205 status = AMediaCodec_releaseOutputBuffer(sys->decoder, sys->currentOutputBufferIndex, FALSE);
206 if (status != AMEDIA_OK)
208 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_releaseOutputBuffer %d", status);
211 sys->currentOutputBufferIndex = -1;
214static int mediacodec_compress(H264_CONTEXT* h264,
const BYTE** pSrcYuv,
const UINT32* pStride,
215 BYTE** ppDstData, UINT32* pDstSize)
218 WINPR_ASSERT(pSrcYuv);
219 WINPR_ASSERT(pStride);
220 WINPR_ASSERT(ppDstData);
221 WINPR_ASSERT(pDstSize);
223 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec is not supported as an encoder");
227static int mediacodec_decompress(H264_CONTEXT* h264,
const BYTE* pSrcData, UINT32 SrcSize)
230 WINPR_ASSERT(pSrcData);
232 H264_CONTEXT_MEDIACODEC* sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
235 BYTE** pYUVData = h264->pYUVData;
236 WINPR_ASSERT(pYUVData);
238 UINT32* iStride = h264->iStride;
239 WINPR_ASSERT(iStride);
241 release_current_outputbuffer(h264);
243 if (sys->width != h264->width || sys->height != h264->height)
245 sys->width = h264->width;
246 sys->height = h264->height;
248 if (sys->width < MEDIACODEC_MINIMUM_WIDTH || sys->height < MEDIACODEC_MINIMUM_HEIGHT)
250 WLog_Print(h264->log, WLOG_ERROR,
251 "MediaCodec got width or height smaller than minimum [%d,%d]", sys->width,
256 WLog_Print(h264->log, WLOG_DEBUG,
"MediaCodec setting new input width and height [%d,%d]",
257 sys->width, sys->height);
260 AMediaFormat_setInt32(sys->inputFormat, AMEDIAFORMAT_KEY_WIDTH, sys->width);
261 AMediaFormat_setInt32(sys->inputFormat, AMEDIAFORMAT_KEY_HEIGHT, sys->height);
262 const media_status_t status = AMediaCodec_setParameters(sys->decoder, sys->inputFormat);
263 if (status != AMEDIA_OK)
265 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_setParameters failed: %d", status);
269 set_mediacodec_format(h264, &sys->inputFormat,
270 mediacodec_format_new(h264->log, sys->width, sys->height));
274 if (update_mediacodec_outputformat(h264) < 0)
276 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec failed updating input format");
283 UINT32 inputBufferCurrnetOffset = 0;
284 while (inputBufferCurrnetOffset < SrcSize)
286 UINT32 numberOfBytesToCopy = SrcSize - inputBufferCurrnetOffset;
287 const ssize_t inputBufferId = AMediaCodec_dequeueInputBuffer(sys->decoder, -1);
288 if (inputBufferId < 0)
290 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_dequeueInputBuffer failed [%d]",
296 size_t inputBufferSize = 0;
297 uint8_t* inputBuffer =
298 AMediaCodec_getInputBuffer(sys->decoder, inputBufferId, &inputBufferSize);
299 if (inputBuffer ==
nullptr)
301 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_getInputBuffer failed");
305 if (numberOfBytesToCopy > inputBufferSize)
307 WLog_Print(h264->log, WLOG_WARN,
308 "MediaCodec inputBufferSize: got [%d] but wanted [%d]", inputBufferSize,
309 numberOfBytesToCopy);
310 numberOfBytesToCopy = inputBufferSize;
313 memcpy(inputBuffer, &pSrcData[inputBufferCurrnetOffset], numberOfBytesToCopy);
314 inputBufferCurrnetOffset += numberOfBytesToCopy;
316 const media_status_t status = AMediaCodec_queueInputBuffer(
317 sys->decoder, inputBufferId, 0, numberOfBytesToCopy, 0, 0);
318 if (status != AMEDIA_OK)
320 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_queueInputBuffer %d", status);
327 AMediaCodecBufferInfo bufferInfo = WINPR_C_ARRAY_INIT;
328 ssize_t outputBufferId = AMediaCodec_dequeueOutputBuffer(sys->decoder, &bufferInfo, -1);
329 if (outputBufferId >= 0)
331 sys->currentOutputBufferIndex = outputBufferId;
333 size_t outputBufferSize = 0;
334 uint8_t* outputBuffer =
nullptr;
336 AMediaCodec_getOutputBuffer(sys->decoder, outputBufferId, &outputBufferSize);
337 sys->currentOutputBufferIndex = outputBufferId;
339 if (outputBufferSize !=
340 (sys->outputWidth * sys->outputHeight +
341 ((sys->outputWidth + 1) / 2) * ((sys->outputHeight + 1) / 2) * 2))
343 WLog_Print(h264->log, WLOG_ERROR,
344 "Error MediaCodec unexpected output buffer size %d",
351 iStride[0] = sys->outputWidth;
352 iStride[1] = (sys->outputWidth + 1) / 2;
353 iStride[2] = (sys->outputWidth + 1) / 2;
354 pYUVData[0] = outputBuffer;
355 pYUVData[1] = outputBuffer + iStride[0] * sys->outputHeight;
356 pYUVData[2] = outputBuffer + iStride[0] * sys->outputHeight +
357 iStride[1] * ((sys->outputHeight + 1) / 2);
359 h264->YUVWidth = sys->outputWidth;
360 h264->YUVHeigth = sys->outputHeigth;
363 else if (outputBufferId == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
365 if (update_mediacodec_outputformat(h264) < 0)
367 WLog_Print(h264->log, WLOG_ERROR,
368 "MediaCodec failed updating output format in decompress");
372 else if (outputBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
374 WLog_Print(h264->log, WLOG_WARN,
375 "AMediaCodec_dequeueOutputBuffer need to try again later");
378 else if (outputBufferId == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)
380 WLog_Print(h264->log, WLOG_WARN,
381 "AMediaCodec_dequeueOutputBuffer returned deprecated value "
382 "AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED, ignoring");
386 WLog_Print(h264->log, WLOG_ERROR,
387 "AMediaCodec_dequeueOutputBuffer returned unknown value [%d]",
399static void mediacodec_uninit(H264_CONTEXT* h264)
401 media_status_t status = AMEDIA_OK;
402 H264_CONTEXT_MEDIACODEC* sys;
406 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
408 WLog_Print(h264->log, WLOG_DEBUG,
"Uninitializing MediaCodec");
413 if (sys->decoder !=
nullptr)
415 release_current_outputbuffer(h264);
416 status = AMediaCodec_stop(sys->decoder);
417 if (status != AMEDIA_OK)
419 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_stop %d", status);
422 status = AMediaCodec_delete(sys->decoder);
423 if (status != AMEDIA_OK)
425 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_delete %d", status);
428 sys->decoder =
nullptr;
431 set_mediacodec_format(h264, &sys->inputFormat,
nullptr);
432 set_mediacodec_format(h264, &sys->outputFormat,
nullptr);
435 h264->pSystemData =
nullptr;
438static BOOL mediacodec_init(H264_CONTEXT* h264)
440 H264_CONTEXT_MEDIACODEC* sys;
441 media_status_t status;
445 if (h264->Compressor)
447 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec is not supported as an encoder");
451 WLog_Print(h264->log, WLOG_DEBUG,
"Initializing MediaCodec");
453 sys = (H264_CONTEXT_MEDIACODEC*)calloc(1,
sizeof(H264_CONTEXT_MEDIACODEC));
460 h264->pSystemData = (
void*)sys;
462 sys->currentOutputBufferIndex = -1;
465 sys->width = sys->outputWidth = MEDIACODEC_MINIMUM_WIDTH;
466 sys->height = sys->outputHeight = MEDIACODEC_MINIMUM_HEIGHT;
467 sys->decoder = AMediaCodec_createDecoderByType(CODEC_NAME);
468 if (sys->decoder ==
nullptr)
470 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_createCodecByName failed");
474#if __ANDROID_API__ >= 28
476 status = AMediaCodec_getName(sys->decoder, &codec_name);
477 if (status != AMEDIA_OK)
479 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_getName failed: %d", status);
483 WLog_Print(h264->log, WLOG_DEBUG,
"MediaCodec using %s codec [%s]", CODEC_NAME, codec_name);
484 AMediaCodec_releaseName(sys->decoder, codec_name);
487 set_mediacodec_format(h264, &sys->inputFormat,
488 mediacodec_format_new(h264->log, sys->width, sys->height));
490 status = AMediaCodec_configure(sys->decoder, sys->inputFormat,
nullptr,
nullptr, 0);
491 if (status != AMEDIA_OK)
493 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_configure failed: %d", status);
497 if (update_mediacodec_inputformat(h264) < 0)
499 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec failed updating input format");
503 if (update_mediacodec_outputformat(h264) < 0)
505 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec failed updating output format");
509 WLog_Print(h264->log, WLOG_DEBUG,
"Starting MediaCodec");
510 status = AMediaCodec_start(sys->decoder);
511 if (status != AMEDIA_OK)
513 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_start failed %d", status);
519 mediacodec_uninit(h264);
523const H264_CONTEXT_SUBSYSTEM g_Subsystem_mediacodec = {
"MediaCodec", mediacodec_init,
524 mediacodec_uninit, mediacodec_decompress,
525 mediacodec_compress };