blob: 79a51ec68406b7668fede5a88c0b59b457ddf189
1 | /* |
2 | * Android MediaCodec H.264 / H.265 / MPEG-4 / VP8 / VP9 decoders |
3 | * |
4 | * Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com> |
5 | * |
6 | * This file is part of FFmpeg. |
7 | * |
8 | * FFmpeg is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * |
13 | * FFmpeg is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with FFmpeg; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ |
22 | |
23 | #include <stdint.h> |
24 | #include <string.h> |
25 | |
26 | #include "libavutil/avassert.h" |
27 | #include "libavutil/common.h" |
28 | #include "libavutil/fifo.h" |
29 | #include "libavutil/opt.h" |
30 | #include "libavutil/intreadwrite.h" |
31 | #include "libavutil/pixfmt.h" |
32 | |
33 | #include "avcodec.h" |
34 | #include "h264_parse.h" |
35 | #include "hevc_parse.h" |
36 | #include "internal.h" |
37 | #include "mediacodec_wrapper.h" |
38 | #include "mediacodecdec_common.h" |
39 | |
40 | typedef struct MediaCodecH264DecContext { |
41 | |
42 | MediaCodecDecContext *ctx; |
43 | |
44 | AVBSFContext *bsf; |
45 | |
46 | AVFifoBuffer *fifo; |
47 | |
48 | AVPacket filtered_pkt; |
49 | |
50 | } MediaCodecH264DecContext; |
51 | |
52 | static av_cold int mediacodec_decode_close(AVCodecContext *avctx) |
53 | { |
54 | MediaCodecH264DecContext *s = avctx->priv_data; |
55 | |
56 | ff_mediacodec_dec_close(avctx, s->ctx); |
57 | s->ctx = NULL; |
58 | |
59 | av_fifo_free(s->fifo); |
60 | |
61 | av_bsf_free(&s->bsf); |
62 | av_packet_unref(&s->filtered_pkt); |
63 | |
64 | return 0; |
65 | } |
66 | |
67 | #if CONFIG_H264_MEDIACODEC_DECODER || CONFIG_HEVC_MEDIACODEC_DECODER |
68 | static int h2645_ps_to_nalu(const uint8_t *src, int src_size, uint8_t **out, int *out_size) |
69 | { |
70 | int i; |
71 | int ret = 0; |
72 | uint8_t *p = NULL; |
73 | static const uint8_t nalu_header[] = { 0x00, 0x00, 0x00, 0x01 }; |
74 | |
75 | if (!out || !out_size) { |
76 | return AVERROR(EINVAL); |
77 | } |
78 | |
79 | p = av_malloc(sizeof(nalu_header) + src_size); |
80 | if (!p) { |
81 | return AVERROR(ENOMEM); |
82 | } |
83 | |
84 | *out = p; |
85 | *out_size = sizeof(nalu_header) + src_size; |
86 | |
87 | memcpy(p, nalu_header, sizeof(nalu_header)); |
88 | memcpy(p + sizeof(nalu_header), src, src_size); |
89 | |
90 | /* Escape 0x00, 0x00, 0x0{0-3} pattern */ |
91 | for (i = 4; i < *out_size; i++) { |
92 | if (i < *out_size - 3 && |
93 | p[i + 0] == 0 && |
94 | p[i + 1] == 0 && |
95 | p[i + 2] <= 3) { |
96 | uint8_t *new; |
97 | |
98 | *out_size += 1; |
99 | new = av_realloc(*out, *out_size); |
100 | if (!new) { |
101 | ret = AVERROR(ENOMEM); |
102 | goto done; |
103 | } |
104 | *out = p = new; |
105 | |
106 | i = i + 2; |
107 | memmove(p + i + 1, p + i, *out_size - (i + 1)); |
108 | p[i] = 0x03; |
109 | } |
110 | } |
111 | done: |
112 | if (ret < 0) { |
113 | av_freep(out); |
114 | *out_size = 0; |
115 | } |
116 | |
117 | return ret; |
118 | } |
119 | #endif |
120 | |
121 | #if CONFIG_H264_MEDIACODEC_DECODER |
122 | static int h264_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) |
123 | { |
124 | int i; |
125 | int ret; |
126 | |
127 | H264ParamSets ps; |
128 | const PPS *pps = NULL; |
129 | const SPS *sps = NULL; |
130 | int is_avc = 0; |
131 | int nal_length_size = 0; |
132 | |
133 | memset(&ps, 0, sizeof(ps)); |
134 | |
135 | ret = ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size, |
136 | &ps, &is_avc, &nal_length_size, 0, avctx); |
137 | if (ret < 0) { |
138 | goto done; |
139 | } |
140 | |
141 | for (i = 0; i < MAX_PPS_COUNT; i++) { |
142 | if (ps.pps_list[i]) { |
143 | pps = (const PPS*)ps.pps_list[i]->data; |
144 | break; |
145 | } |
146 | } |
147 | |
148 | if (pps) { |
149 | if (ps.sps_list[pps->sps_id]) { |
150 | sps = (const SPS*)ps.sps_list[pps->sps_id]->data; |
151 | } |
152 | } |
153 | |
154 | if (pps && sps) { |
155 | uint8_t *data = NULL; |
156 | int data_size = 0; |
157 | |
158 | if ((ret = h2645_ps_to_nalu(sps->data, sps->data_size, &data, &data_size)) < 0) { |
159 | goto done; |
160 | } |
161 | ff_AMediaFormat_setBuffer(format, "csd-0", (void*)data, data_size); |
162 | av_freep(&data); |
163 | |
164 | if ((ret = h2645_ps_to_nalu(pps->data, pps->data_size, &data, &data_size)) < 0) { |
165 | goto done; |
166 | } |
167 | ff_AMediaFormat_setBuffer(format, "csd-1", (void*)data, data_size); |
168 | av_freep(&data); |
169 | } else { |
170 | av_log(avctx, AV_LOG_ERROR, "Could not extract PPS/SPS from extradata"); |
171 | ret = AVERROR_INVALIDDATA; |
172 | } |
173 | |
174 | done: |
175 | ff_h264_ps_uninit(&ps); |
176 | |
177 | return ret; |
178 | } |
179 | #endif |
180 | |
181 | #if CONFIG_HEVC_MEDIACODEC_DECODER |
182 | static int hevc_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) |
183 | { |
184 | int i; |
185 | int ret; |
186 | |
187 | HEVCParamSets ps; |
188 | |
189 | const HEVCVPS *vps = NULL; |
190 | const HEVCPPS *pps = NULL; |
191 | const HEVCSPS *sps = NULL; |
192 | int is_nalff = 0; |
193 | int nal_length_size = 0; |
194 | |
195 | uint8_t *vps_data = NULL; |
196 | uint8_t *sps_data = NULL; |
197 | uint8_t *pps_data = NULL; |
198 | int vps_data_size = 0; |
199 | int sps_data_size = 0; |
200 | int pps_data_size = 0; |
201 | |
202 | memset(&ps, 0, sizeof(ps)); |
203 | |
204 | ret = ff_hevc_decode_extradata(avctx->extradata, avctx->extradata_size, |
205 | &ps, &is_nalff, &nal_length_size, 0, avctx); |
206 | if (ret < 0) { |
207 | goto done; |
208 | } |
209 | |
210 | for (i = 0; i < HEVC_MAX_VPS_COUNT; i++) { |
211 | if (ps.vps_list[i]) { |
212 | vps = (const HEVCVPS*)ps.vps_list[i]->data; |
213 | break; |
214 | } |
215 | } |
216 | |
217 | for (i = 0; i < HEVC_MAX_PPS_COUNT; i++) { |
218 | if (ps.pps_list[i]) { |
219 | pps = (const HEVCPPS*)ps.pps_list[i]->data; |
220 | break; |
221 | } |
222 | } |
223 | |
224 | if (pps) { |
225 | if (ps.sps_list[pps->sps_id]) { |
226 | sps = (const HEVCSPS*)ps.sps_list[pps->sps_id]->data; |
227 | } |
228 | } |
229 | |
230 | if (vps && pps && sps) { |
231 | uint8_t *data; |
232 | int data_size; |
233 | |
234 | if ((ret = h2645_ps_to_nalu(vps->data, vps->data_size, &vps_data, &vps_data_size)) < 0 || |
235 | (ret = h2645_ps_to_nalu(sps->data, sps->data_size, &sps_data, &sps_data_size)) < 0 || |
236 | (ret = h2645_ps_to_nalu(pps->data, pps->data_size, &pps_data, &pps_data_size)) < 0) { |
237 | goto done; |
238 | } |
239 | |
240 | data_size = vps_data_size + sps_data_size + pps_data_size; |
241 | data = av_mallocz(data_size); |
242 | if (!data) { |
243 | ret = AVERROR(ENOMEM); |
244 | goto done; |
245 | } |
246 | |
247 | memcpy(data , vps_data, vps_data_size); |
248 | memcpy(data + vps_data_size , sps_data, sps_data_size); |
249 | memcpy(data + vps_data_size + sps_data_size, pps_data, pps_data_size); |
250 | |
251 | ff_AMediaFormat_setBuffer(format, "csd-0", data, data_size); |
252 | |
253 | av_freep(&data); |
254 | } else { |
255 | av_log(avctx, AV_LOG_ERROR, "Could not extract VPS/PPS/SPS from extradata"); |
256 | ret = AVERROR_INVALIDDATA; |
257 | } |
258 | |
259 | done: |
260 | av_freep(&vps_data); |
261 | av_freep(&sps_data); |
262 | av_freep(&pps_data); |
263 | |
264 | return ret; |
265 | } |
266 | #endif |
267 | |
268 | #if CONFIG_MPEG4_MEDIACODEC_DECODER |
269 | static int mpeg4_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) |
270 | { |
271 | int ret = 0; |
272 | |
273 | if (avctx->extradata) { |
274 | ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size); |
275 | } |
276 | |
277 | return ret; |
278 | } |
279 | #endif |
280 | |
281 | #if CONFIG_VP8_MEDIACODEC_DECODER || CONFIG_VP9_MEDIACODEC_DECODER |
282 | static int vpx_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) |
283 | { |
284 | int ret = 0; |
285 | |
286 | if (avctx->extradata) { |
287 | ff_AMediaFormat_setBuffer(format, "csd-0", avctx->extradata, avctx->extradata_size); |
288 | } |
289 | |
290 | return ret; |
291 | } |
292 | #endif |
293 | |
294 | static av_cold int mediacodec_decode_init(AVCodecContext *avctx) |
295 | { |
296 | int ret; |
297 | |
298 | const char *codec_mime = NULL; |
299 | |
300 | const char *bsf_name = NULL; |
301 | const AVBitStreamFilter *bsf = NULL; |
302 | |
303 | FFAMediaFormat *format = NULL; |
304 | MediaCodecH264DecContext *s = avctx->priv_data; |
305 | |
306 | format = ff_AMediaFormat_new(); |
307 | if (!format) { |
308 | av_log(avctx, AV_LOG_ERROR, "Failed to create media format\n"); |
309 | ret = AVERROR_EXTERNAL; |
310 | goto done; |
311 | } |
312 | |
313 | switch (avctx->codec_id) { |
314 | #if CONFIG_H264_MEDIACODEC_DECODER |
315 | case AV_CODEC_ID_H264: |
316 | codec_mime = "video/avc"; |
317 | bsf_name = "h264_mp4toannexb"; |
318 | |
319 | ret = h264_set_extradata(avctx, format); |
320 | if (ret < 0) |
321 | goto done; |
322 | break; |
323 | #endif |
324 | #if CONFIG_HEVC_MEDIACODEC_DECODER |
325 | case AV_CODEC_ID_HEVC: |
326 | codec_mime = "video/hevc"; |
327 | bsf_name = "hevc_mp4toannexb"; |
328 | |
329 | ret = hevc_set_extradata(avctx, format); |
330 | if (ret < 0) |
331 | goto done; |
332 | break; |
333 | #endif |
334 | #if CONFIG_MPEG4_MEDIACODEC_DECODER |
335 | case AV_CODEC_ID_MPEG4: |
336 | codec_mime = "video/mp4v-es", |
337 | |
338 | ret = mpeg4_set_extradata(avctx, format); |
339 | if (ret < 0) |
340 | goto done; |
341 | break; |
342 | #endif |
343 | #if CONFIG_VP8_MEDIACODEC_DECODER |
344 | case AV_CODEC_ID_VP8: |
345 | codec_mime = "video/x-vnd.on2.vp8"; |
346 | |
347 | ret = vpx_set_extradata(avctx, format); |
348 | if (ret < 0) |
349 | goto done; |
350 | break; |
351 | #endif |
352 | #if CONFIG_VP9_MEDIACODEC_DECODER |
353 | case AV_CODEC_ID_VP9: |
354 | codec_mime = "video/x-vnd.on2.vp9"; |
355 | |
356 | ret = vpx_set_extradata(avctx, format); |
357 | if (ret < 0) |
358 | goto done; |
359 | break; |
360 | #endif |
361 | default: |
362 | av_assert0(0); |
363 | } |
364 | |
365 | ff_AMediaFormat_setString(format, "mime", codec_mime); |
366 | ff_AMediaFormat_setInt32(format, "width", avctx->width); |
367 | ff_AMediaFormat_setInt32(format, "height", avctx->height); |
368 | |
369 | s->ctx = av_mallocz(sizeof(*s->ctx)); |
370 | if (!s->ctx) { |
371 | av_log(avctx, AV_LOG_ERROR, "Failed to allocate MediaCodecDecContext\n"); |
372 | ret = AVERROR(ENOMEM); |
373 | goto done; |
374 | } |
375 | |
376 | if ((ret = ff_mediacodec_dec_init(avctx, s->ctx, codec_mime, format)) < 0) { |
377 | s->ctx = NULL; |
378 | goto done; |
379 | } |
380 | |
381 | av_log(avctx, AV_LOG_INFO, "MediaCodec started successfully, ret = %d\n", ret); |
382 | |
383 | s->fifo = av_fifo_alloc(sizeof(AVPacket)); |
384 | if (!s->fifo) { |
385 | ret = AVERROR(ENOMEM); |
386 | goto done; |
387 | } |
388 | |
389 | if (bsf_name) { |
390 | bsf = av_bsf_get_by_name(bsf_name); |
391 | if(!bsf) { |
392 | ret = AVERROR_BSF_NOT_FOUND; |
393 | goto done; |
394 | } |
395 | |
396 | if ((ret = av_bsf_alloc(bsf, &s->bsf))) { |
397 | goto done; |
398 | } |
399 | |
400 | if (((ret = avcodec_parameters_from_context(s->bsf->par_in, avctx)) < 0) || |
401 | ((ret = av_bsf_init(s->bsf)) < 0)) { |
402 | goto done; |
403 | } |
404 | } |
405 | |
406 | av_init_packet(&s->filtered_pkt); |
407 | |
408 | done: |
409 | if (format) { |
410 | ff_AMediaFormat_delete(format); |
411 | } |
412 | |
413 | if (ret < 0) { |
414 | mediacodec_decode_close(avctx); |
415 | } |
416 | |
417 | return ret; |
418 | } |
419 | |
420 | |
421 | static int mediacodec_process_data(AVCodecContext *avctx, AVFrame *frame, |
422 | int *got_frame, AVPacket *pkt) |
423 | { |
424 | MediaCodecH264DecContext *s = avctx->priv_data; |
425 | |
426 | return ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, pkt); |
427 | } |
428 | |
429 | static int mediacodec_decode_frame(AVCodecContext *avctx, void *data, |
430 | int *got_frame, AVPacket *avpkt) |
431 | { |
432 | MediaCodecH264DecContext *s = avctx->priv_data; |
433 | AVFrame *frame = data; |
434 | int ret; |
435 | |
436 | /* buffer the input packet */ |
437 | if (avpkt->size) { |
438 | AVPacket input_pkt = { 0 }; |
439 | |
440 | if (av_fifo_space(s->fifo) < sizeof(input_pkt)) { |
441 | ret = av_fifo_realloc2(s->fifo, |
442 | av_fifo_size(s->fifo) + sizeof(input_pkt)); |
443 | if (ret < 0) |
444 | return ret; |
445 | } |
446 | |
447 | ret = av_packet_ref(&input_pkt, avpkt); |
448 | if (ret < 0) |
449 | return ret; |
450 | av_fifo_generic_write(s->fifo, &input_pkt, sizeof(input_pkt), NULL); |
451 | } |
452 | |
453 | /* |
454 | * MediaCodec.flush() discards both input and output buffers, thus we |
455 | * need to delay the call to this function until the user has released or |
456 | * renderered the frames he retains. |
457 | * |
458 | * After we have buffered an input packet, check if the codec is in the |
459 | * flushing state. If it is, we need to call ff_mediacodec_dec_flush. |
460 | * |
461 | * ff_mediacodec_dec_flush returns 0 if the flush cannot be performed on |
462 | * the codec (because the user retains frames). The codec stays in the |
463 | * flushing state. |
464 | * |
465 | * ff_mediacodec_dec_flush returns 1 if the flush can actually be |
466 | * performed on the codec. The codec leaves the flushing state and can |
467 | * process again packets. |
468 | * |
469 | * ff_mediacodec_dec_flush returns a negative value if an error has |
470 | * occurred. |
471 | * |
472 | */ |
473 | if (ff_mediacodec_dec_is_flushing(avctx, s->ctx)) { |
474 | if (!ff_mediacodec_dec_flush(avctx, s->ctx)) { |
475 | return avpkt->size; |
476 | } |
477 | } |
478 | |
479 | /* process buffered data */ |
480 | while (!*got_frame) { |
481 | /* prepare the input data -- convert to Annex B if needed */ |
482 | if (s->filtered_pkt.size <= 0) { |
483 | AVPacket input_pkt = { 0 }; |
484 | |
485 | av_packet_unref(&s->filtered_pkt); |
486 | |
487 | /* no more data */ |
488 | if (av_fifo_size(s->fifo) < sizeof(AVPacket)) { |
489 | return avpkt->size ? avpkt->size : |
490 | ff_mediacodec_dec_decode(avctx, s->ctx, frame, got_frame, avpkt); |
491 | } |
492 | |
493 | av_fifo_generic_read(s->fifo, &input_pkt, sizeof(input_pkt), NULL); |
494 | |
495 | if (s->bsf) { |
496 | ret = av_bsf_send_packet(s->bsf, &input_pkt); |
497 | if (ret < 0) { |
498 | return ret; |
499 | } |
500 | |
501 | ret = av_bsf_receive_packet(s->bsf, &s->filtered_pkt); |
502 | if (ret == AVERROR(EAGAIN)) { |
503 | goto done; |
504 | } |
505 | } else { |
506 | av_packet_move_ref(&s->filtered_pkt, &input_pkt); |
507 | } |
508 | |
509 | /* {h264,hevc}_mp4toannexb are used here and do not require flushing */ |
510 | av_assert0(ret != AVERROR_EOF); |
511 | |
512 | if (ret < 0) { |
513 | return ret; |
514 | } |
515 | } |
516 | |
517 | ret = mediacodec_process_data(avctx, frame, got_frame, &s->filtered_pkt); |
518 | if (ret < 0) |
519 | return ret; |
520 | |
521 | s->filtered_pkt.size -= ret; |
522 | s->filtered_pkt.data += ret; |
523 | } |
524 | done: |
525 | return avpkt->size; |
526 | } |
527 | |
528 | static void mediacodec_decode_flush(AVCodecContext *avctx) |
529 | { |
530 | MediaCodecH264DecContext *s = avctx->priv_data; |
531 | |
532 | while (av_fifo_size(s->fifo)) { |
533 | AVPacket pkt; |
534 | av_fifo_generic_read(s->fifo, &pkt, sizeof(pkt), NULL); |
535 | av_packet_unref(&pkt); |
536 | } |
537 | av_fifo_reset(s->fifo); |
538 | |
539 | av_packet_unref(&s->filtered_pkt); |
540 | |
541 | ff_mediacodec_dec_flush(avctx, s->ctx); |
542 | } |
543 | |
544 | #if CONFIG_H264_MEDIACODEC_DECODER |
545 | AVCodec ff_h264_mediacodec_decoder = { |
546 | .name = "h264_mediacodec", |
547 | .long_name = NULL_IF_CONFIG_SMALL("H.264 Android MediaCodec decoder"), |
548 | .type = AVMEDIA_TYPE_VIDEO, |
549 | .id = AV_CODEC_ID_H264, |
550 | .priv_data_size = sizeof(MediaCodecH264DecContext), |
551 | .init = mediacodec_decode_init, |
552 | .decode = mediacodec_decode_frame, |
553 | .flush = mediacodec_decode_flush, |
554 | .close = mediacodec_decode_close, |
555 | .capabilities = CODEC_CAP_DELAY, |
556 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, |
557 | }; |
558 | #endif |
559 | |
560 | #if CONFIG_HEVC_MEDIACODEC_DECODER |
561 | AVCodec ff_hevc_mediacodec_decoder = { |
562 | .name = "hevc_mediacodec", |
563 | .long_name = NULL_IF_CONFIG_SMALL("H.265 Android MediaCodec decoder"), |
564 | .type = AVMEDIA_TYPE_VIDEO, |
565 | .id = AV_CODEC_ID_HEVC, |
566 | .priv_data_size = sizeof(MediaCodecH264DecContext), |
567 | .init = mediacodec_decode_init, |
568 | .decode = mediacodec_decode_frame, |
569 | .flush = mediacodec_decode_flush, |
570 | .close = mediacodec_decode_close, |
571 | .capabilities = CODEC_CAP_DELAY, |
572 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, |
573 | }; |
574 | #endif |
575 | |
576 | #if CONFIG_MPEG4_MEDIACODEC_DECODER |
577 | AVCodec ff_mpeg4_mediacodec_decoder = { |
578 | .name = "mpeg4_mediacodec", |
579 | .long_name = NULL_IF_CONFIG_SMALL("MPEG-4 Android MediaCodec decoder"), |
580 | .type = AVMEDIA_TYPE_VIDEO, |
581 | .id = AV_CODEC_ID_MPEG4, |
582 | .priv_data_size = sizeof(MediaCodecH264DecContext), |
583 | .init = mediacodec_decode_init, |
584 | .decode = mediacodec_decode_frame, |
585 | .flush = mediacodec_decode_flush, |
586 | .close = mediacodec_decode_close, |
587 | .capabilities = CODEC_CAP_DELAY, |
588 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, |
589 | }; |
590 | #endif |
591 | |
592 | #if CONFIG_VP8_MEDIACODEC_DECODER |
593 | AVCodec ff_vp8_mediacodec_decoder = { |
594 | .name = "vp8_mediacodec", |
595 | .long_name = NULL_IF_CONFIG_SMALL("VP8 Android MediaCodec decoder"), |
596 | .type = AVMEDIA_TYPE_VIDEO, |
597 | .id = AV_CODEC_ID_VP8, |
598 | .priv_data_size = sizeof(MediaCodecH264DecContext), |
599 | .init = mediacodec_decode_init, |
600 | .decode = mediacodec_decode_frame, |
601 | .flush = mediacodec_decode_flush, |
602 | .close = mediacodec_decode_close, |
603 | .capabilities = CODEC_CAP_DELAY, |
604 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, |
605 | }; |
606 | #endif |
607 | |
608 | #if CONFIG_VP9_MEDIACODEC_DECODER |
609 | AVCodec ff_vp9_mediacodec_decoder = { |
610 | .name = "vp9_mediacodec", |
611 | .long_name = NULL_IF_CONFIG_SMALL("VP9 Android MediaCodec decoder"), |
612 | .type = AVMEDIA_TYPE_VIDEO, |
613 | .id = AV_CODEC_ID_VP9, |
614 | .priv_data_size = sizeof(MediaCodecH264DecContext), |
615 | .init = mediacodec_decode_init, |
616 | .decode = mediacodec_decode_frame, |
617 | .flush = mediacodec_decode_flush, |
618 | .close = mediacodec_decode_close, |
619 | .capabilities = CODEC_CAP_DELAY, |
620 | .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS, |
621 | }; |
622 | #endif |
623 |