4c2396695988d8162739ecb2cad79d5020d3611e
[vlc.git] / modules / codec / omxil / mediacodec_ndk.c
1 /*****************************************************************************
2  * mediacodec_ndk.c: mc_api implementation using NDK
3  *****************************************************************************
4  * Copyright © 2015 VLC authors and VideoLAN, VideoLabs
5  *
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.
10  *
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.
15  *
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  *****************************************************************************/
20
21 /*****************************************************************************
22  * Preamble
23  *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <jni.h>
29 #include <dlfcn.h>
30 #include <stdint.h>
31 #include <assert.h>
32
33 #include <vlc_common.h>
34
35 #include <OMX_Core.h>
36 #include <OMX_Component.h>
37 #include "omxil_utils.h"
38
39 #include "mediacodec.h"
40
41 char* MediaCodec_GetName(vlc_object_t *p_obj, const char *psz_mime,
42                          size_t h264_profile);
43
44 #define THREAD_NAME "mediacodec_ndk"
45
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
49
50 /*****************************************************************************
51  * NdkMediaError.h
52  *****************************************************************************/
53
54 typedef enum {
55     AMEDIA_OK = 0,
56
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,
63
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,
74
75 } media_status_t;
76
77 /*****************************************************************************
78  * NdkMediaCodec.h
79  *****************************************************************************/
80
81 struct AMediaCodec;
82 typedef struct AMediaCodec AMediaCodec;
83
84 struct AMediaCodecBufferInfo {
85     int32_t offset;
86     int32_t size;
87     int64_t presentationTimeUs;
88     uint32_t flags;
89 };
90 typedef struct AMediaCodecBufferInfo AMediaCodecBufferInfo;
91
92 enum {
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
98 };
99
100 struct AMediaFormat;
101 typedef struct AMediaFormat AMediaFormat;
102
103 struct AMediaCrypto;
104 typedef struct AMediaCrypto AMediaCrypto;
105
106 /*****************************************************************************
107  * Ndk symbols
108  *****************************************************************************/
109
110 typedef AMediaCodec* (*pf_AMediaCodec_createCodecByName)(const char *name);
111
112 typedef media_status_t (*pf_AMediaCodec_configure)(AMediaCodec*,
113         const AMediaFormat* format,
114         ANativeWindow* surface,
115         AMediaCrypto *crypto,
116         uint32_t flags);
117
118 typedef media_status_t (*pf_AMediaCodec_start)(AMediaCodec*);
119
120 typedef media_status_t (*pf_AMediaCodec_stop)(AMediaCodec*);
121
122 typedef media_status_t (*pf_AMediaCodec_flush)(AMediaCodec*);
123
124 typedef media_status_t (*pf_AMediaCodec_delete)(AMediaCodec*);
125
126 typedef AMediaFormat* (*pf_AMediaCodec_getOutputFormat)(AMediaCodec*);
127
128 typedef ssize_t (*pf_AMediaCodec_dequeueInputBuffer)(AMediaCodec*,
129         int64_t timeoutUs);
130
131 typedef uint8_t* (*pf_AMediaCodec_getInputBuffer)(AMediaCodec*,
132         size_t idx, size_t *out_size);
133
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);
136
137 typedef ssize_t (*pf_AMediaCodec_dequeueOutputBuffer)(AMediaCodec*,
138         AMediaCodecBufferInfo *info, int64_t timeoutUs);
139
140 typedef uint8_t* (*pf_AMediaCodec_getOutputBuffer)(AMediaCodec*,
141         size_t idx, size_t *out_size);
142
143 typedef media_status_t (*pf_AMediaCodec_releaseOutputBuffer)(AMediaCodec*,
144         size_t idx, bool render);
145
146 typedef AMediaFormat *(*pf_AMediaFormat_new)();
147 typedef media_status_t (*pf_AMediaFormat_delete)(AMediaFormat*);
148
149 typedef void (*pf_AMediaFormat_setString)(AMediaFormat*,
150         const char* name, const char* value);
151
152 typedef void (*pf_AMediaFormat_setInt32)(AMediaFormat*,
153         const char* name, int32_t value);
154
155 typedef bool (*pf_AMediaFormat_getInt32)(AMediaFormat*,
156         const char *name, int32_t *out);
157
158 struct syms
159 {
160     struct {
161         pf_AMediaCodec_createCodecByName createCodecByName;
162         pf_AMediaCodec_configure configure;
163         pf_AMediaCodec_start start;
164         pf_AMediaCodec_stop stop;
165         pf_AMediaCodec_flush flush;
166         pf_AMediaCodec_delete delete;
167         pf_AMediaCodec_getOutputFormat getOutputFormat;
168         pf_AMediaCodec_dequeueInputBuffer dequeueInputBuffer;
169         pf_AMediaCodec_getInputBuffer getInputBuffer;
170         pf_AMediaCodec_queueInputBuffer queueInputBuffer;
171         pf_AMediaCodec_dequeueOutputBuffer dequeueOutputBuffer;
172         pf_AMediaCodec_getOutputBuffer getOutputBuffer;
173         pf_AMediaCodec_releaseOutputBuffer releaseOutputBuffer;
174     } AMediaCodec;
175     struct {
176         pf_AMediaFormat_new new;
177         pf_AMediaFormat_delete delete;
178         pf_AMediaFormat_setString setString;
179         pf_AMediaFormat_setInt32 setInt32;
180         pf_AMediaFormat_getInt32 getInt32;
181     } AMediaFormat;
182 };
183 static struct syms syms;
184
185 struct members
186 {
187     const char *name;
188     int offset;
189     bool critical;
190 };
191 static struct members members[] =
192 {
193 #define OFF(x) offsetof(struct syms, AMediaCodec.x)
194     { "AMediaCodec_createCodecByName", OFF(createCodecByName), true },
195     { "AMediaCodec_configure", OFF(configure), true },
196     { "AMediaCodec_start", OFF(start), true },
197     { "AMediaCodec_stop", OFF(stop), true },
198     { "AMediaCodec_flush", OFF(flush), true },
199     { "AMediaCodec_delete", OFF(delete), true },
200     { "AMediaCodec_getOutputFormat", OFF(getOutputFormat), true },
201     { "AMediaCodec_dequeueInputBuffer", OFF(dequeueInputBuffer), true },
202     { "AMediaCodec_getInputBuffer", OFF(getInputBuffer), true },
203     { "AMediaCodec_queueInputBuffer", OFF(queueInputBuffer), true },
204     { "AMediaCodec_dequeueOutputBuffer", OFF(dequeueOutputBuffer), true },
205     { "AMediaCodec_getOutputBuffer", OFF(getOutputBuffer), true },
206     { "AMediaCodec_releaseOutputBuffer", OFF(releaseOutputBuffer), true },
207 #undef OFF
208 #define OFF(x) offsetof(struct syms, AMediaFormat.x)
209     { "AMediaFormat_new", OFF(new), true },
210     { "AMediaFormat_delete", OFF(delete), true },
211     { "AMediaFormat_setString", OFF(setString), true },
212     { "AMediaFormat_setInt32", OFF(setInt32), true },
213     { "AMediaFormat_getInt32", OFF(getInt32), true },
214 #undef OFF
215     { NULL, 0, false }
216 };
217 #undef OFF
218
219 /* Initialize all symbols.
220  * Done only one time during the first initialisation */
221 static bool
222 InitSymbols(mc_api *api)
223 {
224     static vlc_mutex_t lock = VLC_STATIC_MUTEX;
225     static int i_init_state = -1;
226     bool ret;
227
228     vlc_mutex_lock(&lock);
229
230     if (i_init_state != -1)
231         goto end;
232
233     i_init_state = 0;
234
235     void *ndk_handle = dlopen("libmediandk.so", RTLD_NOW);
236     if (!ndk_handle)
237         goto end;
238
239     for (int i = 0; members[i].name; i++)
240     {
241         void *sym = dlsym(ndk_handle, members[i].name);
242         if (!sym && members[i].critical)
243         {
244             dlclose(ndk_handle);
245             goto end;
246         }
247         *(void **)((uint8_t*)&syms + members[i].offset) = sym;
248     }
249
250     i_init_state = 1;
251 end:
252     ret = i_init_state == 1;
253     if (!ret)
254         msg_Err(api->p_obj, "MediaCodec NDK init failed");
255
256     vlc_mutex_unlock(&lock);
257     return ret;
258 }
259
260 /****************************************************************************
261  * Local prototypes
262  ****************************************************************************/
263
264 struct mc_api_sys
265 {
266     AMediaCodec* p_codec;
267     AMediaFormat* p_format;
268     AMediaCodecBufferInfo info;
269 };
270
271 /*****************************************************************************
272  * Stop
273  *****************************************************************************/
274 static int Stop(mc_api *api)
275 {
276     mc_api_sys *p_sys = api->p_sys;
277
278     api->b_direct_rendering = false;
279
280     if (p_sys->p_codec)
281     {
282         if (api->b_started)
283         {
284             syms.AMediaCodec.stop(p_sys->p_codec);
285             api->b_started = false;
286         }
287         syms.AMediaCodec.delete(p_sys->p_codec);
288         p_sys->p_codec = NULL;
289     }
290     if (p_sys->p_format)
291     {
292         syms.AMediaFormat.delete(p_sys->p_format);
293         p_sys->p_format = NULL;
294     }
295
296     msg_Dbg(api->p_obj, "MediaCodec via NDK closed");
297     return 0;
298 }
299
300 /*****************************************************************************
301  * Start
302  *****************************************************************************/
303 static int Start(mc_api *api, union mc_api_args *p_args)
304 {
305     mc_api_sys *p_sys = api->p_sys;
306     int i_ret = MC_API_ERROR;
307     ANativeWindow *p_anw = NULL;
308
309     assert(api->psz_mime && api->psz_name);
310
311     p_sys->p_codec = syms.AMediaCodec.createCodecByName(api->psz_name);
312     if (!p_sys->p_codec)
313     {
314         msg_Err(api->p_obj, "AMediaCodec.createCodecByName for %s failed",
315                 api->psz_name);
316         goto error;
317     }
318
319     p_sys->p_format = syms.AMediaFormat.new();
320     if (!p_sys->p_format)
321     {
322         msg_Err(api->p_obj, "AMediaFormat.new failed");
323         goto error;
324     }
325
326     syms.AMediaFormat.setInt32(p_sys->p_format, "encoder", 0);
327     syms.AMediaFormat.setString(p_sys->p_format, "mime", api->psz_mime);
328     /* No limits for input size */
329     syms.AMediaFormat.setInt32(p_sys->p_format, "max-input-size", 0);
330     if (api->i_cat == VIDEO_ES)
331     {
332         syms.AMediaFormat.setInt32(p_sys->p_format, "width", p_args->video.i_width);
333         syms.AMediaFormat.setInt32(p_sys->p_format, "height", p_args->video.i_height);
334         syms.AMediaFormat.setInt32(p_sys->p_format, "rotation-degrees", p_args->video.i_angle);
335         if (p_args->video.p_awh)
336         {
337             p_anw = AWindowHandler_getANativeWindow(p_args->video.p_awh,
338                                                     AWindow_Video);
339             if( p_anw )
340                 syms.AMediaFormat.setInt32(p_sys->p_format,
341                                            "feature-tunneled-playback",
342                                            p_args->video.b_tunneled_playback);
343         }
344     }
345     else
346     {
347         syms.AMediaFormat.setInt32(p_sys->p_format, "sample-rate", p_args->audio.i_sample_rate);
348         syms.AMediaFormat.setInt32(p_sys->p_format, "channel-count", p_args->audio.i_channel_count);
349     }
350
351     if (syms.AMediaCodec.configure(p_sys->p_codec, p_sys->p_format,
352                                    p_anw, NULL, 0) != AMEDIA_OK)
353     {
354         msg_Err(api->p_obj, "AMediaCodec.configure failed");
355         goto error;
356     }
357     if (syms.AMediaCodec.start(p_sys->p_codec) != AMEDIA_OK)
358     {
359         msg_Err(api->p_obj, "AMediaCodec.start failed");
360         goto error;
361     }
362
363     api->b_started = true;
364     api->b_direct_rendering = !!p_anw;
365     i_ret = 0;
366
367     msg_Dbg(api->p_obj, "MediaCodec via NDK opened");
368 error:
369     if (i_ret != 0)
370         Stop(api);
371     return i_ret;
372 }
373
374 /*****************************************************************************
375  * Flush
376  *****************************************************************************/
377 static int Flush(mc_api *api)
378 {
379     mc_api_sys *p_sys = api->p_sys;
380
381     if (syms.AMediaCodec.flush(p_sys->p_codec) == AMEDIA_OK)
382         return 0;
383     else
384         return MC_API_ERROR;
385 }
386
387 /*****************************************************************************
388  * DequeueInput
389  *****************************************************************************/
390 static int DequeueInput(mc_api *api, mtime_t i_timeout)
391 {
392     mc_api_sys *p_sys = api->p_sys;
393     ssize_t i_index;
394
395     i_index = syms.AMediaCodec.dequeueInputBuffer(p_sys->p_codec, i_timeout);
396     if (i_index >= 0)
397         return i_index;
398     else if (i_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
399         return MC_API_INFO_TRYAGAIN;
400     else
401     {
402         msg_Err(api->p_obj, "AMediaCodec.dequeueInputBuffer failed");
403         return MC_API_ERROR;
404     }
405 }
406
407 /*****************************************************************************
408  * QueueInput
409  *****************************************************************************/
410 static int QueueInput(mc_api *api, int i_index, const void *p_buf,
411                       size_t i_size, mtime_t i_ts, bool b_config)
412 {
413     mc_api_sys *p_sys = api->p_sys;
414     uint8_t *p_mc_buf;
415     size_t i_mc_size;
416     int i_flags = (b_config ? AMEDIACODEC_FLAG_CODEC_CONFIG : 0)
417                 | (p_buf == NULL ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
418
419     assert(i_index >= 0);
420
421     p_mc_buf = syms.AMediaCodec.getInputBuffer(p_sys->p_codec,
422                                                i_index, &i_mc_size);
423     if (!p_mc_buf)
424         return MC_API_ERROR;
425
426     if (i_mc_size > i_size)
427         i_mc_size = i_size;
428     memcpy(p_mc_buf, p_buf, i_mc_size);
429
430     if (syms.AMediaCodec.queueInputBuffer(p_sys->p_codec, i_index, 0, i_mc_size,
431                                           i_ts, i_flags) == AMEDIA_OK)
432         return 0;
433     else
434     {
435         msg_Err(api->p_obj, "AMediaCodec.queueInputBuffer failed");
436         return MC_API_ERROR;
437     }
438 }
439
440 static int32_t GetFormatInteger(AMediaFormat *p_format, const char *psz_name)
441 {
442     int32_t i_out = 0;
443     syms.AMediaFormat.getInt32(p_format, psz_name, &i_out);
444     return i_out;
445 }
446
447 /*****************************************************************************
448  * DequeueOutput
449  *****************************************************************************/
450 static int DequeueOutput(mc_api *api, mtime_t i_timeout)
451 {
452     mc_api_sys *p_sys = api->p_sys;
453     ssize_t i_index;
454
455     i_index = syms.AMediaCodec.dequeueOutputBuffer(p_sys->p_codec, &p_sys->info,
456                                                    i_timeout);
457
458     if (i_index >= 0)
459         return i_index;
460     else if (i_index == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
461         return MC_API_INFO_TRYAGAIN;
462     else if (i_index == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)
463         return MC_API_INFO_OUTPUT_BUFFERS_CHANGED;
464     else if (i_index == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
465         return MC_API_INFO_OUTPUT_FORMAT_CHANGED;
466     else
467     {
468         msg_Err(api->p_obj, "AMediaCodec.dequeueOutputBuffer failed");
469         return MC_API_ERROR;
470     }
471 }
472
473 /*****************************************************************************
474  * GetOutput
475  *****************************************************************************/
476 static int GetOutput(mc_api *api, int i_index, mc_api_out *p_out)
477 {
478     mc_api_sys *p_sys = api->p_sys;
479
480     if (i_index >= 0)
481     {
482         p_out->type = MC_OUT_TYPE_BUF;
483         p_out->u.buf.i_index = i_index;
484
485         p_out->u.buf.i_ts = p_sys->info.presentationTimeUs;
486         p_out->b_eos = p_sys->info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
487
488         if (api->b_direct_rendering)
489         {
490             p_out->u.buf.p_ptr = NULL;
491             p_out->u.buf.i_size = 0;
492         }
493         else
494         {
495             size_t i_mc_size;
496             uint8_t *p_mc_buf = syms.AMediaCodec.getOutputBuffer(p_sys->p_codec,
497                                                                  i_index,
498                                                                  &i_mc_size);
499             /* p_mc_buf can be NULL in case of EOS */
500             if (!p_mc_buf && !p_out->b_eos)
501             {
502                 msg_Err(api->p_obj, "AMediaCodec.getOutputBuffer failed");
503                 return MC_API_ERROR;
504             }
505             p_out->u.buf.p_ptr = p_mc_buf + p_sys->info.offset;
506             p_out->u.buf.i_size = p_sys->info.size;
507         }
508         return 1;
509     }
510     else if (i_index == MC_API_INFO_OUTPUT_FORMAT_CHANGED)
511     {
512         AMediaFormat *format = syms.AMediaCodec.getOutputFormat(p_sys->p_codec);
513
514         p_out->type = MC_OUT_TYPE_CONF;
515         p_out->b_eos = false;
516         if (api->i_cat == VIDEO_ES)
517         {
518             p_out->u.conf.video.width         = GetFormatInteger(format, "width");
519             p_out->u.conf.video.height        = GetFormatInteger(format, "height");
520             p_out->u.conf.video.stride        = GetFormatInteger(format, "stride");
521             p_out->u.conf.video.slice_height  = GetFormatInteger(format, "slice-height");
522             p_out->u.conf.video.pixel_format  = GetFormatInteger(format, "color-format");
523             p_out->u.conf.video.crop_left     = GetFormatInteger(format, "crop-left");
524             p_out->u.conf.video.crop_top      = GetFormatInteger(format, "crop-top");
525             p_out->u.conf.video.crop_right    = GetFormatInteger(format, "crop-right");
526             p_out->u.conf.video.crop_bottom   = GetFormatInteger(format, "crop-bottom");
527         }
528         else
529         {
530             p_out->u.conf.audio.channel_count = GetFormatInteger(format, "channel-count");
531             p_out->u.conf.audio.channel_mask  = GetFormatInteger(format, "channel-mask");
532             p_out->u.conf.audio.sample_rate   = GetFormatInteger(format, "sample-rate");
533         }
534         return 1;
535     }
536     return 0;
537 }
538
539 /*****************************************************************************
540  * ReleaseOutput
541  *****************************************************************************/
542 static int ReleaseOutput(mc_api *api, int i_index, bool b_render)
543 {
544     mc_api_sys *p_sys = api->p_sys;
545
546     assert(i_index >= 0);
547     if (syms.AMediaCodec.releaseOutputBuffer(p_sys->p_codec, i_index, b_render)
548                                              == AMEDIA_OK)
549         return 0;
550     else
551         return MC_API_ERROR;
552 }
553
554
555 /*****************************************************************************
556  * Clean
557  *****************************************************************************/
558 static void Clean(mc_api *api)
559 {
560     free(api->psz_name);
561     free(api->p_sys);
562 }
563
564 /*****************************************************************************
565  * Configure
566  *****************************************************************************/
567 static int Configure(mc_api * api, size_t i_h264_profile)
568 {
569     free(api->psz_name);
570     api->psz_name = MediaCodec_GetName(api->p_obj, api->psz_mime,
571                                        i_h264_profile);
572     if (!api->psz_name)
573         return MC_API_ERROR;
574     api->i_quirks = OMXCodec_GetQuirks(api->i_cat, api->i_codec, api->psz_name,
575                                        strlen(api->psz_name));
576     return 0;
577 }
578
579 /*****************************************************************************
580  * MediaCodecNdk_Init
581  *****************************************************************************/
582 int MediaCodecNdk_Init(mc_api *api)
583 {
584     if (!InitSymbols(api))
585         return MC_API_ERROR;
586
587     api->p_sys = calloc(1, sizeof(mc_api_sys));
588     if (!api->p_sys)
589         return MC_API_ERROR;
590
591     api->clean = Clean;
592     api->configure = Configure;
593     api->start = Start;
594     api->stop = Stop;
595     api->flush = Flush;
596     api->dequeue_in = DequeueInput;
597     api->queue_in = QueueInput;
598     api->dequeue_out = DequeueOutput;
599     api->get_out = GetOutput;
600     api->release_out = ReleaseOutput;
601
602     api->b_support_interlaced = true;
603     return 0;
604 }