1 /*****************************************************************************
2 * mediacodec_ndk.c: mc_api implementation using NDK
3 *****************************************************************************
4 * Copyright © 2015 VLC authors and VideoLAN, VideoLabs
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 /*****************************************************************************
23 *****************************************************************************/
33 #include <vlc_common.h>
36 #include <OMX_Component.h>
37 #include "omxil_utils.h"
39 #include "mediacodec.h"
41 char* MediaCodec_GetName(vlc_object_t
*p_obj
, const char *psz_mime
,
42 int hxxx_profile
, bool *p_adaptive
);
44 #define THREAD_NAME "mediacodec_ndk"
46 /* Not in NdkMedia API but we need it since we send config data via input
47 * buffers and not via "csd-*" buffers from AMediaFormat */
48 #define AMEDIACODEC_FLAG_CODEC_CONFIG 2
50 /*****************************************************************************
52 *****************************************************************************/
57 AMEDIA_ERROR_BASE
= -10000,
58 AMEDIA_ERROR_UNKNOWN
= AMEDIA_ERROR_BASE
,
59 AMEDIA_ERROR_MALFORMED
= AMEDIA_ERROR_BASE
- 1,
60 AMEDIA_ERROR_UNSUPPORTED
= AMEDIA_ERROR_BASE
- 2,
61 AMEDIA_ERROR_INVALID_OBJECT
= AMEDIA_ERROR_BASE
- 3,
62 AMEDIA_ERROR_INVALID_PARAMETER
= AMEDIA_ERROR_BASE
- 4,
64 AMEDIA_DRM_ERROR_BASE
= -20000,
65 AMEDIA_DRM_NOT_PROVISIONED
= AMEDIA_DRM_ERROR_BASE
- 1,
66 AMEDIA_DRM_RESOURCE_BUSY
= AMEDIA_DRM_ERROR_BASE
- 2,
67 AMEDIA_DRM_DEVICE_REVOKED
= AMEDIA_DRM_ERROR_BASE
- 3,
68 AMEDIA_DRM_SHORT_BUFFER
= AMEDIA_DRM_ERROR_BASE
- 4,
69 AMEDIA_DRM_SESSION_NOT_OPENED
= AMEDIA_DRM_ERROR_BASE
- 5,
70 AMEDIA_DRM_TAMPER_DETECTED
= AMEDIA_DRM_ERROR_BASE
- 6,
71 AMEDIA_DRM_VERIFY_FAILED
= AMEDIA_DRM_ERROR_BASE
- 7,
72 AMEDIA_DRM_NEED_KEY
= AMEDIA_DRM_ERROR_BASE
- 8,
73 AMEDIA_DRM_LICENSE_EXPIRED
= AMEDIA_DRM_ERROR_BASE
- 9,
77 /*****************************************************************************
79 *****************************************************************************/
82 typedef struct AMediaCodec AMediaCodec
;
84 struct AMediaCodecBufferInfo
{
87 int64_t presentationTimeUs
;
90 typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo
;
93 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM
= 4,
94 AMEDIACODEC_CONFIGURE_FLAG_ENCODE
= 1,
95 AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED
= -3,
96 AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
= -2,
97 AMEDIACODEC_INFO_TRY_AGAIN_LATER
= -1
101 typedef struct AMediaFormat AMediaFormat
;
104 typedef struct AMediaCrypto AMediaCrypto
;
106 /*****************************************************************************
108 *****************************************************************************/
110 typedef AMediaCodec
* (*pf_AMediaCodec_createCodecByName
)(const char *name
);
112 typedef media_status_t (*pf_AMediaCodec_configure
)(AMediaCodec
*,
113 const AMediaFormat
* format
,
114 ANativeWindow
* surface
,
115 AMediaCrypto
*crypto
,
118 typedef media_status_t (*pf_AMediaCodec_start
)(AMediaCodec
*);
120 typedef media_status_t (*pf_AMediaCodec_stop
)(AMediaCodec
*);
122 typedef media_status_t (*pf_AMediaCodec_flush
)(AMediaCodec
*);
124 typedef media_status_t (*pf_AMediaCodec_delete
)(AMediaCodec
*);
126 typedef AMediaFormat
* (*pf_AMediaCodec_getOutputFormat
)(AMediaCodec
*);
128 typedef ssize_t (*pf_AMediaCodec_dequeueInputBuffer
)(AMediaCodec
*,
131 typedef uint8_t* (*pf_AMediaCodec_getInputBuffer
)(AMediaCodec
*,
132 size_t idx
, size_t *out_size
);
134 typedef media_status_t (*pf_AMediaCodec_queueInputBuffer
)(AMediaCodec
*,
135 size_t idx
, off_t offset
, size_t size
, uint64_t time
, uint32_t flags
);
137 typedef ssize_t (*pf_AMediaCodec_dequeueOutputBuffer
)(AMediaCodec
*,
138 AMediaCodecBufferInfo
*info
, int64_t timeoutUs
);
140 typedef uint8_t* (*pf_AMediaCodec_getOutputBuffer
)(AMediaCodec
*,
141 size_t idx
, size_t *out_size
);
143 typedef media_status_t (*pf_AMediaCodec_releaseOutputBuffer
)(AMediaCodec
*,
144 size_t idx
, bool render
);
146 typedef media_status_t (*pf_AMediaCodec_releaseOutputBufferAtTime
)(AMediaCodec
*,
147 size_t idx
, int64_t timestampNs
);
149 typedef media_status_t (*pf_AMediaCodec_setOutputSurface
)(AMediaCodec
*,
150 ANativeWindow
*surface
);
152 typedef AMediaFormat
*(*pf_AMediaFormat_new
)();
153 typedef media_status_t (*pf_AMediaFormat_delete
)(AMediaFormat
*);
155 typedef void (*pf_AMediaFormat_setString
)(AMediaFormat
*,
156 const char* name
, const char* value
);
158 typedef void (*pf_AMediaFormat_setInt32
)(AMediaFormat
*,
159 const char* name
, int32_t value
);
161 typedef bool (*pf_AMediaFormat_getInt32
)(AMediaFormat
*,
162 const char *name
, int32_t *out
);
167 pf_AMediaCodec_createCodecByName createCodecByName
;
168 pf_AMediaCodec_configure configure
;
169 pf_AMediaCodec_start start
;
170 pf_AMediaCodec_stop stop
;
171 pf_AMediaCodec_flush flush
;
172 pf_AMediaCodec_delete
delete;
173 pf_AMediaCodec_getOutputFormat getOutputFormat
;
174 pf_AMediaCodec_dequeueInputBuffer dequeueInputBuffer
;
175 pf_AMediaCodec_getInputBuffer getInputBuffer
;
176 pf_AMediaCodec_queueInputBuffer queueInputBuffer
;
177 pf_AMediaCodec_dequeueOutputBuffer dequeueOutputBuffer
;
178 pf_AMediaCodec_getOutputBuffer getOutputBuffer
;
179 pf_AMediaCodec_releaseOutputBuffer releaseOutputBuffer
;
180 pf_AMediaCodec_releaseOutputBufferAtTime releaseOutputBufferAtTime
;
181 pf_AMediaCodec_setOutputSurface setOutputSurface
;
184 pf_AMediaFormat_new
new;
185 pf_AMediaFormat_delete
delete;
186 pf_AMediaFormat_setString setString
;
187 pf_AMediaFormat_setInt32 setInt32
;
188 pf_AMediaFormat_getInt32 getInt32
;
191 static struct syms syms
;
199 static struct members members
[] =
201 #define OFF(x) offsetof(struct syms, AMediaCodec.x)
202 { "AMediaCodec_createCodecByName", OFF(createCodecByName
), true },
203 { "AMediaCodec_configure", OFF(configure
), true },
204 { "AMediaCodec_start", OFF(start
), true },
205 { "AMediaCodec_stop", OFF(stop
), true },
206 { "AMediaCodec_flush", OFF(flush
), true },
207 { "AMediaCodec_delete", OFF(delete), true },
208 { "AMediaCodec_getOutputFormat", OFF(getOutputFormat
), true },
209 { "AMediaCodec_dequeueInputBuffer", OFF(dequeueInputBuffer
), true },
210 { "AMediaCodec_getInputBuffer", OFF(getInputBuffer
), true },
211 { "AMediaCodec_queueInputBuffer", OFF(queueInputBuffer
), true },
212 { "AMediaCodec_dequeueOutputBuffer", OFF(dequeueOutputBuffer
), true },
213 { "AMediaCodec_getOutputBuffer", OFF(getOutputBuffer
), true },
214 { "AMediaCodec_releaseOutputBuffer", OFF(releaseOutputBuffer
), true },
215 { "AMediaCodec_releaseOutputBufferAtTime", OFF(releaseOutputBufferAtTime
), true },
216 { "AMediaCodec_setOutputSurface", OFF(setOutputSurface
), false },
218 #define OFF(x) offsetof(struct syms, AMediaFormat.x)
219 { "AMediaFormat_new", OFF(new), true },
220 { "AMediaFormat_delete", OFF(delete), true },
221 { "AMediaFormat_setString", OFF(setString
), true },
222 { "AMediaFormat_setInt32", OFF(setInt32
), true },
223 { "AMediaFormat_getInt32", OFF(getInt32
), true },
229 /* Initialize all symbols.
230 * Done only one time during the first initialisation */
232 InitSymbols(mc_api
*api
)
234 static vlc_mutex_t lock
= VLC_STATIC_MUTEX
;
235 static int i_init_state
= -1;
238 vlc_mutex_lock(&lock
);
240 if (i_init_state
!= -1)
245 void *ndk_handle
= dlopen("libmediandk.so", RTLD_NOW
);
249 for (int i
= 0; members
[i
].name
; i
++)
251 void *sym
= dlsym(ndk_handle
, members
[i
].name
);
252 if (!sym
&& members
[i
].critical
)
257 *(void **)((uint8_t*)&syms
+ members
[i
].offset
) = sym
;
262 ret
= i_init_state
== 1;
264 msg_Err(api
->p_obj
, "MediaCodec NDK init failed");
266 vlc_mutex_unlock(&lock
);
270 /****************************************************************************
272 ****************************************************************************/
276 AMediaCodec
* p_codec
;
277 AMediaFormat
* p_format
;
278 AMediaCodecBufferInfo info
;
281 /*****************************************************************************
283 *****************************************************************************/
284 static int ConfigureDecoder(mc_api
*api
, union mc_api_args
*p_args
)
286 mc_api_sys
*p_sys
= api
->p_sys
;
287 ANativeWindow
*p_anw
= NULL
;
289 assert(api
->psz_mime
&& api
->psz_name
);
291 p_sys
->p_codec
= syms
.AMediaCodec
.createCodecByName(api
->psz_name
);
294 msg_Err(api
->p_obj
, "AMediaCodec.createCodecByName for %s failed",
299 p_sys
->p_format
= syms
.AMediaFormat
.new();
300 if (!p_sys
->p_format
)
302 msg_Err(api
->p_obj
, "AMediaFormat.new failed");
306 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "encoder", 0);
307 syms
.AMediaFormat
.setString(p_sys
->p_format
, "mime", api
->psz_mime
);
308 /* No limits for input size */
309 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "max-input-size", 0);
310 if (api
->i_cat
== VIDEO_ES
)
312 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "width", p_args
->video
.i_width
);
313 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "height", p_args
->video
.i_height
);
314 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "rotation-degrees", p_args
->video
.i_angle
);
315 if (p_args
->video
.p_surface
)
317 p_anw
= p_args
->video
.p_surface
;
318 if (p_args
->video
.b_tunneled_playback
)
319 syms
.AMediaFormat
.setInt32(p_sys
->p_format
,
320 "feature-tunneled-playback", 1);
321 if (p_args
->video
.b_adaptive_playback
)
322 syms
.AMediaFormat
.setInt32(p_sys
->p_format
,
323 "feature-adaptive-playback", 1);
328 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "sample-rate", p_args
->audio
.i_sample_rate
);
329 syms
.AMediaFormat
.setInt32(p_sys
->p_format
, "channel-count", p_args
->audio
.i_channel_count
);
332 if (syms
.AMediaCodec
.configure(p_sys
->p_codec
, p_sys
->p_format
,
333 p_anw
, NULL
, 0) != AMEDIA_OK
)
335 msg_Err(api
->p_obj
, "AMediaCodec.configure failed");
339 api
->b_direct_rendering
= !!p_anw
;
344 /*****************************************************************************
346 *****************************************************************************/
347 static int Stop(mc_api
*api
)
349 mc_api_sys
*p_sys
= api
->p_sys
;
351 api
->b_direct_rendering
= false;
357 syms
.AMediaCodec
.stop(p_sys
->p_codec
);
358 api
->b_started
= false;
360 syms
.AMediaCodec
.delete(p_sys
->p_codec
);
361 p_sys
->p_codec
= NULL
;
365 syms
.AMediaFormat
.delete(p_sys
->p_format
);
366 p_sys
->p_format
= NULL
;
369 msg_Dbg(api
->p_obj
, "MediaCodec via NDK closed");
373 /*****************************************************************************
375 *****************************************************************************/
376 static int Start(mc_api
*api
)
378 mc_api_sys
*p_sys
= api
->p_sys
;
379 int i_ret
= MC_API_ERROR
;
381 if (syms
.AMediaCodec
.start(p_sys
->p_codec
) != AMEDIA_OK
)
383 msg_Err(api
->p_obj
, "AMediaCodec.start failed");
387 api
->b_started
= true;
390 msg_Dbg(api
->p_obj
, "MediaCodec via NDK opened");
397 /*****************************************************************************
399 *****************************************************************************/
400 static int Flush(mc_api
*api
)
402 mc_api_sys
*p_sys
= api
->p_sys
;
404 if (syms
.AMediaCodec
.flush(p_sys
->p_codec
) == AMEDIA_OK
)
410 /*****************************************************************************
412 *****************************************************************************/
413 static int DequeueInput(mc_api
*api
, vlc_tick_t i_timeout
)
415 mc_api_sys
*p_sys
= api
->p_sys
;
418 i_index
= syms
.AMediaCodec
.dequeueInputBuffer(p_sys
->p_codec
, i_timeout
);
421 else if (i_index
== AMEDIACODEC_INFO_TRY_AGAIN_LATER
)
422 return MC_API_INFO_TRYAGAIN
;
425 msg_Err(api
->p_obj
, "AMediaCodec.dequeueInputBuffer failed");
430 /*****************************************************************************
432 *****************************************************************************/
433 static int QueueInput(mc_api
*api
, int i_index
, const void *p_buf
,
434 size_t i_size
, vlc_tick_t i_ts
, bool b_config
)
436 mc_api_sys
*p_sys
= api
->p_sys
;
439 int i_flags
= (b_config
? AMEDIACODEC_FLAG_CODEC_CONFIG
: 0)
440 | (p_buf
== NULL
? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM
: 0);
442 assert(i_index
>= 0);
444 p_mc_buf
= syms
.AMediaCodec
.getInputBuffer(p_sys
->p_codec
,
445 i_index
, &i_mc_size
);
449 if (i_mc_size
> i_size
)
451 memcpy(p_mc_buf
, p_buf
, i_mc_size
);
453 if (syms
.AMediaCodec
.queueInputBuffer(p_sys
->p_codec
, i_index
, 0, i_mc_size
,
454 i_ts
, i_flags
) == AMEDIA_OK
)
458 msg_Err(api
->p_obj
, "AMediaCodec.queueInputBuffer failed");
463 static int32_t GetFormatInteger(AMediaFormat
*p_format
, const char *psz_name
)
466 syms
.AMediaFormat
.getInt32(p_format
, psz_name
, &i_out
);
470 /*****************************************************************************
472 *****************************************************************************/
473 static int DequeueOutput(mc_api
*api
, vlc_tick_t i_timeout
)
475 mc_api_sys
*p_sys
= api
->p_sys
;
478 i_index
= syms
.AMediaCodec
.dequeueOutputBuffer(p_sys
->p_codec
, &p_sys
->info
,
483 else if (i_index
== AMEDIACODEC_INFO_TRY_AGAIN_LATER
)
484 return MC_API_INFO_TRYAGAIN
;
485 else if (i_index
== AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED
)
486 return MC_API_INFO_OUTPUT_BUFFERS_CHANGED
;
487 else if (i_index
== AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED
)
488 return MC_API_INFO_OUTPUT_FORMAT_CHANGED
;
491 msg_Warn(api
->p_obj
, "AMediaCodec.dequeueOutputBuffer failed");
496 /*****************************************************************************
498 *****************************************************************************/
499 static int GetOutput(mc_api
*api
, int i_index
, mc_api_out
*p_out
)
501 mc_api_sys
*p_sys
= api
->p_sys
;
505 p_out
->type
= MC_OUT_TYPE_BUF
;
506 p_out
->buf
.i_index
= i_index
;
508 p_out
->buf
.i_ts
= p_sys
->info
.presentationTimeUs
;
509 p_out
->b_eos
= p_sys
->info
.flags
& AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM
;
511 if (api
->b_direct_rendering
)
513 p_out
->buf
.p_ptr
= NULL
;
514 p_out
->buf
.i_size
= 0;
519 uint8_t *p_mc_buf
= syms
.AMediaCodec
.getOutputBuffer(p_sys
->p_codec
,
522 /* p_mc_buf can be NULL in case of EOS */
523 if (!p_mc_buf
&& !p_out
->b_eos
)
525 msg_Err(api
->p_obj
, "AMediaCodec.getOutputBuffer failed");
528 p_out
->buf
.p_ptr
= p_mc_buf
+ p_sys
->info
.offset
;
529 p_out
->buf
.i_size
= p_sys
->info
.size
;
533 else if (i_index
== MC_API_INFO_OUTPUT_FORMAT_CHANGED
)
535 AMediaFormat
*format
= syms
.AMediaCodec
.getOutputFormat(p_sys
->p_codec
);
537 p_out
->type
= MC_OUT_TYPE_CONF
;
538 p_out
->b_eos
= false;
539 if (api
->i_cat
== VIDEO_ES
)
541 p_out
->conf
.video
.width
= GetFormatInteger(format
, "width");
542 p_out
->conf
.video
.height
= GetFormatInteger(format
, "height");
543 p_out
->conf
.video
.stride
= GetFormatInteger(format
, "stride");
544 p_out
->conf
.video
.slice_height
= GetFormatInteger(format
, "slice-height");
545 p_out
->conf
.video
.pixel_format
= GetFormatInteger(format
, "color-format");
546 p_out
->conf
.video
.crop_left
= GetFormatInteger(format
, "crop-left");
547 p_out
->conf
.video
.crop_top
= GetFormatInteger(format
, "crop-top");
548 p_out
->conf
.video
.crop_right
= GetFormatInteger(format
, "crop-right");
549 p_out
->conf
.video
.crop_bottom
= GetFormatInteger(format
, "crop-bottom");
553 p_out
->conf
.audio
.channel_count
= GetFormatInteger(format
, "channel-count");
554 p_out
->conf
.audio
.channel_mask
= GetFormatInteger(format
, "channel-mask");
555 p_out
->conf
.audio
.sample_rate
= GetFormatInteger(format
, "sample-rate");
562 /*****************************************************************************
564 *****************************************************************************/
565 static int ReleaseOutput(mc_api
*api
, int i_index
, bool b_render
)
567 mc_api_sys
*p_sys
= api
->p_sys
;
569 assert(i_index
>= 0);
570 if (syms
.AMediaCodec
.releaseOutputBuffer(p_sys
->p_codec
, i_index
, b_render
)
577 /*****************************************************************************
578 * ReleaseOutputAtTime
579 *****************************************************************************/
580 static int ReleaseOutputAtTime(mc_api
*api
, int i_index
, int64_t i_ts_ns
)
582 mc_api_sys
*p_sys
= api
->p_sys
;
584 assert(i_index
>= 0);
585 if (syms
.AMediaCodec
.releaseOutputBufferAtTime(p_sys
->p_codec
, i_index
, i_ts_ns
)
592 /*****************************************************************************
594 *****************************************************************************/
595 static int SetOutputSurface(mc_api
*api
, void *p_surface
, void *p_jsurface
)
598 assert(p_surface
!= NULL
);
599 mc_api_sys
*p_sys
= api
->p_sys
;
601 return syms
.AMediaCodec
.setOutputSurface
!= NULL
602 && syms
.AMediaCodec
.setOutputSurface(p_sys
->p_codec
, p_surface
)
603 == AMEDIA_OK
? 0 : MC_API_ERROR
;
606 /*****************************************************************************
608 *****************************************************************************/
609 static void Clean(mc_api
*api
)
615 /*****************************************************************************
617 *****************************************************************************/
618 static int Prepare(mc_api
* api
, int i_profile
)
622 api
->psz_name
= MediaCodec_GetName(api
->p_obj
, api
->psz_mime
,
623 i_profile
, &b_adaptive
);
626 api
->i_quirks
= OMXCodec_GetQuirks(api
->i_cat
, api
->i_codec
, api
->psz_name
,
627 strlen(api
->psz_name
));
628 /* Allow interlaced picture after API 21 */
629 api
->i_quirks
|= MC_API_VIDEO_QUIRKS_SUPPORT_INTERLACED
;
631 api
->i_quirks
|= MC_API_VIDEO_QUIRKS_ADAPTIVE
;
635 /*****************************************************************************
637 *****************************************************************************/
638 int MediaCodecNdk_Init(mc_api
*api
)
640 if (!InitSymbols(api
))
643 api
->p_sys
= calloc(1, sizeof(mc_api_sys
));
648 api
->prepare
= Prepare
;
649 api
->configure_decoder
= ConfigureDecoder
;
653 api
->dequeue_in
= DequeueInput
;
654 api
->queue_in
= QueueInput
;
655 api
->dequeue_out
= DequeueOutput
;
656 api
->get_out
= GetOutput
;
657 api
->release_out
= ReleaseOutput
;
658 api
->release_out_ts
= ReleaseOutputAtTime
;
659 api
->set_output_surface
= SetOutputSurface
;
661 api
->b_support_rotation
= true;