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>
32 static const char* CODEC_NAME =
"video/avc";
34 static const int COLOR_FormatYUV420Planar = 19;
35 static const int COLOR_FormatYUV420Flexible = 0x7f420888;
38 static const int MEDIACODEC_MINIMUM_WIDTH = 320;
39 static const int MEDIACODEC_MINIMUM_HEIGHT = 240;
44 AMediaFormat* inputFormat;
45 AMediaFormat* outputFormat;
50 ssize_t currentOutputBufferIndex;
51 } H264_CONTEXT_MEDIACODEC;
53 static AMediaFormat* mediacodec_format_new(wLog* log,
int width,
int height)
55 const char* media_format;
56 AMediaFormat* format = AMediaFormat_new();
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 == NULL)
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]",
82 static 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 != NULL)
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;
109 static 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;
120 #if __ANDROID__ >= 21
121 inputFormat = AMediaCodec_getInputFormat(sys->decoder);
122 if (inputFormat == NULL)
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 == NULL)
135 WLog_Print(h264->log, WLOG_ERROR,
"AMediaFormat_toString failed");
138 WLog_Print(h264->log, WLOG_DEBUG,
"Using MediaCodec with input MediaFormat [%s]",
144 static 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 == NULL)
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 == NULL)
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;
191 static 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;
214 static 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");
227 static int mediacodec_decompress(H264_CONTEXT* h264,
const BYTE* pSrcData, UINT32 SrcSize)
229 ssize_t inputBufferId = -1;
230 size_t inputBufferSize, outputBufferSize;
231 uint8_t* inputBuffer;
232 media_status_t status;
235 H264_CONTEXT_MEDIACODEC* sys;
238 WINPR_ASSERT(pSrcData);
240 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
243 pYUVData = h264->pYUVData;
244 WINPR_ASSERT(pYUVData);
246 iStride = h264->iStride;
247 WINPR_ASSERT(iStride);
249 release_current_outputbuffer(h264);
251 if (sys->width != h264->width || sys->height != h264->height)
253 sys->width = h264->width;
254 sys->height = h264->height;
256 if (sys->width < MEDIACODEC_MINIMUM_WIDTH || sys->height < MEDIACODEC_MINIMUM_HEIGHT)
258 WLog_Print(h264->log, WLOG_ERROR,
259 "MediaCodec got width or height smaller than minimum [%d,%d]", sys->width,
264 WLog_Print(h264->log, WLOG_DEBUG,
"MediaCodec setting new input width and height [%d,%d]",
265 sys->width, sys->height);
267 #if __ANDROID__ >= 26
268 AMediaFormat_setInt32(sys->inputFormat, AMEDIAFORMAT_KEY_WIDTH, sys->width);
269 AMediaFormat_setInt32(sys->inputFormat, AMEDIAFORMAT_KEY_HEIGHT, sys->height);
270 status = AMediaCodec_setParameters(sys->decoder, sys->inputFormat);
271 if (status != AMEDIA_OK)
273 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_setParameters failed: %d", status);
277 set_mediacodec_format(h264, &sys->inputFormat,
278 mediacodec_format_new(h264->log, sys->width, sys->height));
282 if (update_mediacodec_outputformat(h264) < 0)
284 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec failed updating input format");
291 UINT32 inputBufferCurrnetOffset = 0;
292 while (inputBufferCurrnetOffset < SrcSize)
294 UINT32 numberOfBytesToCopy = SrcSize - inputBufferCurrnetOffset;
295 inputBufferId = AMediaCodec_dequeueInputBuffer(sys->decoder, -1);
296 if (inputBufferId < 0)
298 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_dequeueInputBuffer failed [%d]",
304 inputBuffer = AMediaCodec_getInputBuffer(sys->decoder, inputBufferId, &inputBufferSize);
305 if (inputBuffer == NULL)
307 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_getInputBuffer failed");
311 if (numberOfBytesToCopy > inputBufferSize)
313 WLog_Print(h264->log, WLOG_WARN,
314 "MediaCodec inputBufferSize: got [%d] but wanted [%d]", inputBufferSize,
315 numberOfBytesToCopy);
316 numberOfBytesToCopy = inputBufferSize;
319 memcpy(inputBuffer, pSrcData + inputBufferCurrnetOffset, numberOfBytesToCopy);
320 inputBufferCurrnetOffset += numberOfBytesToCopy;
322 status = AMediaCodec_queueInputBuffer(sys->decoder, inputBufferId, 0,
323 numberOfBytesToCopy, 0, 0);
324 if (status != AMEDIA_OK)
326 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_queueInputBuffer %d", status);
333 AMediaCodecBufferInfo bufferInfo;
334 ssize_t outputBufferId = AMediaCodec_dequeueOutputBuffer(sys->decoder, &bufferInfo, -1);
335 if (outputBufferId >= 0)
337 sys->currentOutputBufferIndex = outputBufferId;
339 uint8_t* outputBuffer;
341 AMediaCodec_getOutputBuffer(sys->decoder, outputBufferId, &outputBufferSize);
342 sys->currentOutputBufferIndex = outputBufferId;
344 if (outputBufferSize !=
345 (sys->outputWidth * sys->outputHeight +
346 ((sys->outputWidth + 1) / 2) * ((sys->outputHeight + 1) / 2) * 2))
348 WLog_Print(h264->log, WLOG_ERROR,
349 "Error MediaCodec unexpected output buffer size %d",
356 iStride[0] = sys->outputWidth;
357 iStride[1] = (sys->outputWidth + 1) / 2;
358 iStride[2] = (sys->outputWidth + 1) / 2;
359 pYUVData[0] = outputBuffer;
360 pYUVData[1] = outputBuffer + iStride[0] * sys->outputHeight;
361 pYUVData[2] = outputBuffer + iStride[0] * sys->outputHeight +
362 iStride[1] * ((sys->outputHeight + 1) / 2);
365 else if (outputBufferId == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
367 if (update_mediacodec_outputformat(h264) < 0)
369 WLog_Print(h264->log, WLOG_ERROR,
370 "MediaCodec failed updating output format in decompress");
374 else if (outputBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
376 WLog_Print(h264->log, WLOG_WARN,
377 "AMediaCodec_dequeueOutputBuffer need to try again later");
380 else if (outputBufferId == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)
382 WLog_Print(h264->log, WLOG_WARN,
383 "AMediaCodec_dequeueOutputBuffer returned deprecated value "
384 "AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED, ignoring");
388 WLog_Print(h264->log, WLOG_ERROR,
389 "AMediaCodec_dequeueOutputBuffer returned unknown value [%d]",
401 static void mediacodec_uninit(H264_CONTEXT* h264)
403 media_status_t status = AMEDIA_OK;
404 H264_CONTEXT_MEDIACODEC* sys;
408 sys = (H264_CONTEXT_MEDIACODEC*)h264->pSystemData;
410 WLog_Print(h264->log, WLOG_DEBUG,
"Uninitializing MediaCodec");
415 if (sys->decoder != NULL)
417 release_current_outputbuffer(h264);
418 status = AMediaCodec_stop(sys->decoder);
419 if (status != AMEDIA_OK)
421 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_stop %d", status);
424 status = AMediaCodec_delete(sys->decoder);
425 if (status != AMEDIA_OK)
427 WLog_Print(h264->log, WLOG_ERROR,
"Error AMediaCodec_delete %d", status);
433 set_mediacodec_format(h264, &sys->inputFormat, NULL);
434 set_mediacodec_format(h264, &sys->outputFormat, NULL);
437 h264->pSystemData = NULL;
440 static BOOL mediacodec_init(H264_CONTEXT* h264)
442 H264_CONTEXT_MEDIACODEC* sys;
443 media_status_t status;
447 if (h264->Compressor)
449 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec is not supported as an encoder");
453 WLog_Print(h264->log, WLOG_DEBUG,
"Initializing MediaCodec");
455 sys = (H264_CONTEXT_MEDIACODEC*)calloc(1,
sizeof(H264_CONTEXT_MEDIACODEC));
462 h264->pSystemData = (
void*)sys;
464 sys->currentOutputBufferIndex = -1;
467 sys->width = sys->outputWidth = MEDIACODEC_MINIMUM_WIDTH;
468 sys->height = sys->outputHeight = MEDIACODEC_MINIMUM_HEIGHT;
469 sys->decoder = AMediaCodec_createDecoderByType(CODEC_NAME);
470 if (sys->decoder == NULL)
472 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_createCodecByName failed");
476 #if __ANDROID_API__ >= 28
478 status = AMediaCodec_getName(sys->decoder, &codec_name);
479 if (status != AMEDIA_OK)
481 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_getName failed: %d", status);
485 WLog_Print(h264->log, WLOG_DEBUG,
"MediaCodec using %s codec [%s]", CODEC_NAME, codec_name);
486 AMediaCodec_releaseName(sys->decoder, codec_name);
489 set_mediacodec_format(h264, &sys->inputFormat,
490 mediacodec_format_new(h264->log, sys->width, sys->height));
492 status = AMediaCodec_configure(sys->decoder, sys->inputFormat, NULL, NULL, 0);
493 if (status != AMEDIA_OK)
495 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_configure failed: %d", status);
499 if (update_mediacodec_inputformat(h264) < 0)
501 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec failed updating input format");
505 if (update_mediacodec_outputformat(h264) < 0)
507 WLog_Print(h264->log, WLOG_ERROR,
"MediaCodec failed updating output format");
511 WLog_Print(h264->log, WLOG_DEBUG,
"Starting MediaCodec");
512 status = AMediaCodec_start(sys->decoder);
513 if (status != AMEDIA_OK)
515 WLog_Print(h264->log, WLOG_ERROR,
"AMediaCodec_start failed %d", status);
521 mediacodec_uninit(h264);
525 const H264_CONTEXT_SUBSYSTEM g_Subsystem_mediacodec = {
"MediaCodec", mediacodec_init,
526 mediacodec_uninit, mediacodec_decompress,
527 mediacodec_compress };