wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 1 | /* |
| 2 | * MMAL Video Decoder |
rcombs | eabf5e6 | 2021-01-20 07:02:56 | [diff] [blame] | 3 | * Copyright (c) 2015 rcombs |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 4 | * |
Michael Niedermayer | 268ff17 | 2015-03-29 09:32:24 | [diff] [blame] | 5 | * This file is part of FFmpeg. |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 6 | * |
Michael Niedermayer | 268ff17 | 2015-03-29 09:32:24 | [diff] [blame] | 7 | * FFmpeg is free software; you can redistribute it and/or |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
Michael Niedermayer | 268ff17 | 2015-03-29 09:32:24 | [diff] [blame] | 12 | * FFmpeg is distributed in the hope that it will be useful, |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
Michael Niedermayer | 268ff17 | 2015-03-29 09:32:24 | [diff] [blame] | 18 | * License along with FFmpeg; if not, write to the Free Software |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ |
| 21 | |
| 22 | /** |
| 23 | * @file |
| 24 | * MMAL Video Decoder |
| 25 | */ |
| 26 | |
wm4 | 2de7650 | 2015-04-13 17:17:59 | [diff] [blame] | 27 | #include <bcm_host.h> |
| 28 | #include <interface/mmal/mmal.h> |
wm4 | b7ab6e1 | 2015-09-08 17:42:24 | [diff] [blame] | 29 | #include <interface/mmal/mmal_parameters_video.h> |
wm4 | 2de7650 | 2015-04-13 17:17:59 | [diff] [blame] | 30 | #include <interface/mmal/util/mmal_util.h> |
| 31 | #include <interface/mmal/util/mmal_util_params.h> |
| 32 | #include <interface/mmal/util/mmal_default_components.h> |
wm4 | 49e531f | 2015-04-13 17:18:00 | [diff] [blame] | 33 | #include <interface/mmal/vc/mmal_vc_api.h> |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 34 | #include <stdatomic.h> |
wm4 | 2de7650 | 2015-04-13 17:17:59 | [diff] [blame] | 35 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 36 | #include "avcodec.h" |
Andreas Rheinhardt | a688f3c | 2022-03-16 17:18:28 | [diff] [blame] | 37 | #include "codec_internal.h" |
James Almer | c56d787 | 2020-08-16 14:43:48 | [diff] [blame] | 38 | #include "decode.h" |
Mark Thompson | 2594f6a | 2020-04-13 15:33:14 | [diff] [blame] | 39 | #include "hwconfig.h" |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 40 | #include "internal.h" |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 41 | #include "libavutil/avassert.h" |
| 42 | #include "libavutil/buffer.h" |
| 43 | #include "libavutil/common.h" |
Martin Storsjö | b8e899f | 2016-04-12 07:48:10 | [diff] [blame] | 44 | #include "libavutil/imgutils.h" |
Andreas Rheinhardt | 790f793 | 2024-03-25 00:30:37 | [diff] [blame] | 45 | #include "libavutil/mem.h" |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 46 | #include "libavutil/opt.h" |
| 47 | #include "libavutil/log.h" |
| 48 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 49 | typedef struct FFBufferEntry { |
| 50 | AVBufferRef *ref; |
| 51 | void *data; |
| 52 | size_t length; |
| 53 | int64_t pts, dts; |
| 54 | int flags; |
| 55 | struct FFBufferEntry *next; |
| 56 | } FFBufferEntry; |
| 57 | |
| 58 | // MMAL_POOL_T destroys all of its MMAL_BUFFER_HEADER_Ts. If we want correct |
| 59 | // refcounting for AVFrames, we can free the MMAL_POOL_T only after all AVFrames |
| 60 | // have been unreferenced. |
| 61 | typedef struct FFPoolRef { |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 62 | atomic_int refcount; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 63 | MMAL_POOL_T *pool; |
| 64 | } FFPoolRef; |
| 65 | |
| 66 | typedef struct FFBufferRef { |
| 67 | MMAL_BUFFER_HEADER_T *buffer; |
| 68 | FFPoolRef *pool; |
| 69 | } FFBufferRef; |
| 70 | |
| 71 | typedef struct MMALDecodeContext { |
| 72 | AVClass *av_class; |
| 73 | int extra_buffers; |
wm4 | 14a90c9 | 2016-01-28 16:24:53 | [diff] [blame] | 74 | int extra_decoder_buffers; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 75 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 76 | MMAL_COMPONENT_T *decoder; |
| 77 | MMAL_QUEUE_T *queue_decoded_frames; |
| 78 | MMAL_POOL_T *pool_in; |
| 79 | FFPoolRef *pool_out; |
| 80 | |
| 81 | // Waiting input packets. Because the libavcodec API requires decoding and |
| 82 | // returning packets in lockstep, it can happen that queue_decoded_frames |
| 83 | // contains almost all surfaces - then the decoder input queue can quickly |
| 84 | // fill up and won't accept new input either. Without consuming input, the |
| 85 | // libavcodec API can't return new frames, and we have a logical deadlock. |
| 86 | // This is avoided by queuing such buffers here. |
| 87 | FFBufferEntry *waiting_buffers, *waiting_buffers_tail; |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 88 | /* Packet used to hold received packets temporarily; not owned by us. */ |
| 89 | AVPacket *pkt; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 90 | |
| 91 | int64_t packets_sent; |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 92 | atomic_int packets_buffered; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 93 | int64_t frames_output; |
| 94 | int eos_received; |
| 95 | int eos_sent; |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 96 | int extradata_sent; |
Jens Ziller | bc7066f | 2016-08-14 14:44:39 | [diff] [blame] | 97 | int interlaced_frame; |
| 98 | int top_field_first; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 99 | } MMALDecodeContext; |
| 100 | |
| 101 | // Assume decoder is guaranteed to produce output after at least this many |
| 102 | // packets (where each packet contains 1 frame). |
| 103 | #define MAX_DELAYED_FRAMES 16 |
| 104 | |
Andreas Rheinhardt | 687a287 | 2024-02-05 18:58:20 | [diff] [blame] | 105 | static const enum AVPixelFormat mmal_pixfmts[] = { |
| 106 | AV_PIX_FMT_MMAL, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE |
| 107 | }; |
| 108 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 109 | static void ffmmal_poolref_unref(FFPoolRef *ref) |
| 110 | { |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 111 | if (ref && |
| 112 | atomic_fetch_add_explicit(&ref->refcount, -1, memory_order_acq_rel) == 1) { |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 113 | mmal_pool_destroy(ref->pool); |
| 114 | av_free(ref); |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | static void ffmmal_release_frame(void *opaque, uint8_t *data) |
| 119 | { |
| 120 | FFBufferRef *ref = (void *)data; |
| 121 | |
| 122 | mmal_buffer_header_release(ref->buffer); |
| 123 | ffmmal_poolref_unref(ref->pool); |
| 124 | |
| 125 | av_free(ref); |
| 126 | } |
| 127 | |
| 128 | // Setup frame with a new reference to buffer. The buffer must have been |
| 129 | // allocated from the given pool. |
| 130 | static int ffmmal_set_ref(AVFrame *frame, FFPoolRef *pool, |
| 131 | MMAL_BUFFER_HEADER_T *buffer) |
| 132 | { |
| 133 | FFBufferRef *ref = av_mallocz(sizeof(*ref)); |
| 134 | if (!ref) |
| 135 | return AVERROR(ENOMEM); |
| 136 | |
| 137 | ref->pool = pool; |
| 138 | ref->buffer = buffer; |
| 139 | |
| 140 | frame->buf[0] = av_buffer_create((void *)ref, sizeof(*ref), |
| 141 | ffmmal_release_frame, NULL, |
| 142 | AV_BUFFER_FLAG_READONLY); |
| 143 | if (!frame->buf[0]) { |
| 144 | av_free(ref); |
| 145 | return AVERROR(ENOMEM); |
| 146 | } |
| 147 | |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 148 | atomic_fetch_add_explicit(&ref->pool->refcount, 1, memory_order_relaxed); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 149 | mmal_buffer_header_acquire(buffer); |
| 150 | |
| 151 | frame->format = AV_PIX_FMT_MMAL; |
| 152 | frame->data[3] = (uint8_t *)ref->buffer; |
| 153 | return 0; |
| 154 | } |
| 155 | |
| 156 | static void ffmmal_stop_decoder(AVCodecContext *avctx) |
| 157 | { |
| 158 | MMALDecodeContext *ctx = avctx->priv_data; |
| 159 | MMAL_COMPONENT_T *decoder = ctx->decoder; |
| 160 | MMAL_BUFFER_HEADER_T *buffer; |
| 161 | |
| 162 | mmal_port_disable(decoder->input[0]); |
| 163 | mmal_port_disable(decoder->output[0]); |
| 164 | mmal_port_disable(decoder->control); |
| 165 | |
| 166 | mmal_port_flush(decoder->input[0]); |
| 167 | mmal_port_flush(decoder->output[0]); |
| 168 | mmal_port_flush(decoder->control); |
| 169 | |
| 170 | while ((buffer = mmal_queue_get(ctx->queue_decoded_frames))) |
| 171 | mmal_buffer_header_release(buffer); |
| 172 | |
| 173 | while (ctx->waiting_buffers) { |
| 174 | FFBufferEntry *buffer = ctx->waiting_buffers; |
| 175 | |
| 176 | ctx->waiting_buffers = buffer->next; |
| 177 | |
wm4 | a55fbfa | 2015-11-06 12:02:16 | [diff] [blame] | 178 | if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 179 | atomic_fetch_add(&ctx->packets_buffered, -1); |
wm4 | a55fbfa | 2015-11-06 12:02:16 | [diff] [blame] | 180 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 181 | av_buffer_unref(&buffer->ref); |
| 182 | av_free(buffer); |
| 183 | } |
| 184 | ctx->waiting_buffers_tail = NULL; |
| 185 | |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 186 | av_assert0(atomic_load(&ctx->packets_buffered) == 0); |
wm4 | 39f01e3 | 2015-09-23 18:27:25 | [diff] [blame] | 187 | |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 188 | ctx->frames_output = ctx->eos_received = ctx->eos_sent = ctx->packets_sent = ctx->extradata_sent = 0; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | static av_cold int ffmmal_close_decoder(AVCodecContext *avctx) |
| 192 | { |
| 193 | MMALDecodeContext *ctx = avctx->priv_data; |
| 194 | |
| 195 | if (ctx->decoder) |
| 196 | ffmmal_stop_decoder(avctx); |
| 197 | |
| 198 | mmal_component_destroy(ctx->decoder); |
| 199 | ctx->decoder = NULL; |
| 200 | mmal_queue_destroy(ctx->queue_decoded_frames); |
| 201 | mmal_pool_destroy(ctx->pool_in); |
| 202 | ffmmal_poolref_unref(ctx->pool_out); |
| 203 | |
wm4 | 49e531f | 2015-04-13 17:18:00 | [diff] [blame] | 204 | mmal_vc_deinit(); |
| 205 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 206 | return 0; |
| 207 | } |
| 208 | |
| 209 | static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 210 | { |
wm4 | 39f01e3 | 2015-09-23 18:27:25 | [diff] [blame] | 211 | AVCodecContext *avctx = (AVCodecContext*)port->userdata; |
| 212 | MMALDecodeContext *ctx = avctx->priv_data; |
| 213 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 214 | if (!buffer->cmd) { |
wm4 | 65db489 | 2015-09-23 18:27:24 | [diff] [blame] | 215 | FFBufferEntry *entry = buffer->user_data; |
| 216 | av_buffer_unref(&entry->ref); |
wm4 | 39f01e3 | 2015-09-23 18:27:25 | [diff] [blame] | 217 | if (entry->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 218 | atomic_fetch_add(&ctx->packets_buffered, -1); |
wm4 | 65db489 | 2015-09-23 18:27:24 | [diff] [blame] | 219 | av_free(entry); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 220 | } |
| 221 | mmal_buffer_header_release(buffer); |
| 222 | } |
| 223 | |
| 224 | static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 225 | { |
| 226 | AVCodecContext *avctx = (AVCodecContext*)port->userdata; |
| 227 | MMALDecodeContext *ctx = avctx->priv_data; |
| 228 | |
| 229 | mmal_queue_put(ctx->queue_decoded_frames, buffer); |
| 230 | } |
| 231 | |
| 232 | static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 233 | { |
| 234 | AVCodecContext *avctx = (AVCodecContext*)port->userdata; |
| 235 | MMAL_STATUS_T status; |
| 236 | |
| 237 | if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 238 | status = *(uint32_t *)buffer->data; |
| 239 | av_log(avctx, AV_LOG_ERROR, "MMAL error %d on control port\n", (int)status); |
| 240 | } else { |
Clément Bœsch | 67e370e | 2017-03-26 23:31:37 | [diff] [blame] | 241 | av_log(avctx, AV_LOG_WARNING, "Unknown MMAL event %s on control port\n", |
| 242 | av_fourcc2str(buffer->cmd)); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 243 | } |
| 244 | |
| 245 | mmal_buffer_header_release(buffer); |
| 246 | } |
| 247 | |
| 248 | // Feed free output buffers to the decoder. |
| 249 | static int ffmmal_fill_output_port(AVCodecContext *avctx) |
| 250 | { |
| 251 | MMALDecodeContext *ctx = avctx->priv_data; |
| 252 | MMAL_BUFFER_HEADER_T *buffer; |
| 253 | MMAL_STATUS_T status; |
| 254 | |
| 255 | if (!ctx->pool_out) |
| 256 | return AVERROR_UNKNOWN; // format change code failed with OOM previously |
| 257 | |
| 258 | while ((buffer = mmal_queue_get(ctx->pool_out->pool->queue))) { |
| 259 | if ((status = mmal_port_send_buffer(ctx->decoder->output[0], buffer))) { |
| 260 | mmal_buffer_header_release(buffer); |
| 261 | av_log(avctx, AV_LOG_ERROR, "MMAL error %d when sending output buffer.\n", (int)status); |
| 262 | return AVERROR_UNKNOWN; |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | return 0; |
| 267 | } |
| 268 | |
| 269 | static enum AVColorSpace ffmmal_csp_to_av_csp(MMAL_FOURCC_T fourcc) |
| 270 | { |
| 271 | switch (fourcc) { |
| 272 | case MMAL_COLOR_SPACE_BT470_2_BG: |
| 273 | case MMAL_COLOR_SPACE_BT470_2_M: |
| 274 | case MMAL_COLOR_SPACE_ITUR_BT601: return AVCOL_SPC_BT470BG; |
| 275 | case MMAL_COLOR_SPACE_ITUR_BT709: return AVCOL_SPC_BT709; |
| 276 | case MMAL_COLOR_SPACE_FCC: return AVCOL_SPC_FCC; |
| 277 | case MMAL_COLOR_SPACE_SMPTE240M: return AVCOL_SPC_SMPTE240M; |
| 278 | default: return AVCOL_SPC_UNSPECIFIED; |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | static int ffmal_update_format(AVCodecContext *avctx) |
| 283 | { |
| 284 | MMALDecodeContext *ctx = avctx->priv_data; |
| 285 | MMAL_STATUS_T status; |
| 286 | int ret = 0; |
| 287 | MMAL_COMPONENT_T *decoder = ctx->decoder; |
| 288 | MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format; |
Jens Ziller | bc7066f | 2016-08-14 14:44:39 | [diff] [blame] | 289 | MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 290 | |
| 291 | ffmmal_poolref_unref(ctx->pool_out); |
| 292 | if (!(ctx->pool_out = av_mallocz(sizeof(*ctx->pool_out)))) { |
| 293 | ret = AVERROR(ENOMEM); |
| 294 | goto fail; |
| 295 | } |
Wan-Teh Chang | d82d537 | 2016-11-30 19:52:41 | [diff] [blame] | 296 | atomic_init(&ctx->pool_out->refcount, 1); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 297 | |
| 298 | if (!format_out) |
| 299 | goto fail; |
| 300 | |
| 301 | if ((status = mmal_port_parameter_set_uint32(decoder->output[0], MMAL_PARAMETER_EXTRA_BUFFERS, ctx->extra_buffers))) |
| 302 | goto fail; |
| 303 | |
wm4 | b7ab6e1 | 2015-09-08 17:42:24 | [diff] [blame] | 304 | if ((status = mmal_port_parameter_set_boolean(decoder->output[0], MMAL_PARAMETER_VIDEO_INTERPOLATE_TIMESTAMPS, 0))) |
| 305 | goto fail; |
| 306 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 307 | if (avctx->pix_fmt == AV_PIX_FMT_MMAL) { |
| 308 | format_out->encoding = MMAL_ENCODING_OPAQUE; |
| 309 | } else { |
| 310 | format_out->encoding_variant = format_out->encoding = MMAL_ENCODING_I420; |
| 311 | } |
| 312 | |
| 313 | if ((status = mmal_port_format_commit(decoder->output[0]))) |
| 314 | goto fail; |
| 315 | |
Jens Ziller | bc7066f | 2016-08-14 14:44:39 | [diff] [blame] | 316 | interlace_type.hdr.id = MMAL_PARAMETER_VIDEO_INTERLACE_TYPE; |
| 317 | interlace_type.hdr.size = sizeof(MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T); |
| 318 | status = mmal_port_parameter_get(decoder->output[0], &interlace_type.hdr); |
| 319 | if (status != MMAL_SUCCESS) { |
| 320 | av_log(avctx, AV_LOG_ERROR, "Cannot read MMAL interlace information!\n"); |
| 321 | } else { |
| 322 | ctx->interlaced_frame = (interlace_type.eMode != MMAL_InterlaceProgressive); |
| 323 | ctx->top_field_first = (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst); |
| 324 | } |
| 325 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 326 | if ((ret = ff_set_dimensions(avctx, format_out->es->video.crop.x + format_out->es->video.crop.width, |
| 327 | format_out->es->video.crop.y + format_out->es->video.crop.height)) < 0) |
| 328 | goto fail; |
| 329 | |
| 330 | if (format_out->es->video.par.num && format_out->es->video.par.den) { |
| 331 | avctx->sample_aspect_ratio.num = format_out->es->video.par.num; |
| 332 | avctx->sample_aspect_ratio.den = format_out->es->video.par.den; |
| 333 | } |
Jens Ziller | bc7066f | 2016-08-14 14:44:39 | [diff] [blame] | 334 | if (format_out->es->video.frame_rate.num && format_out->es->video.frame_rate.den) { |
| 335 | avctx->framerate.num = format_out->es->video.frame_rate.num; |
| 336 | avctx->framerate.den = format_out->es->video.frame_rate.den; |
| 337 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 338 | |
| 339 | avctx->colorspace = ffmmal_csp_to_av_csp(format_out->es->video.color_space); |
| 340 | |
| 341 | decoder->output[0]->buffer_size = |
| 342 | FFMAX(decoder->output[0]->buffer_size_min, decoder->output[0]->buffer_size_recommended); |
| 343 | decoder->output[0]->buffer_num = |
| 344 | FFMAX(decoder->output[0]->buffer_num_min, decoder->output[0]->buffer_num_recommended) + ctx->extra_buffers; |
| 345 | ctx->pool_out->pool = mmal_pool_create(decoder->output[0]->buffer_num, |
| 346 | decoder->output[0]->buffer_size); |
| 347 | if (!ctx->pool_out->pool) { |
| 348 | ret = AVERROR(ENOMEM); |
| 349 | goto fail; |
| 350 | } |
| 351 | |
| 352 | return 0; |
| 353 | |
| 354 | fail: |
| 355 | return ret < 0 ? ret : AVERROR_UNKNOWN; |
| 356 | } |
| 357 | |
| 358 | static av_cold int ffmmal_init_decoder(AVCodecContext *avctx) |
| 359 | { |
| 360 | MMALDecodeContext *ctx = avctx->priv_data; |
| 361 | MMAL_STATUS_T status; |
| 362 | MMAL_ES_FORMAT_T *format_in; |
| 363 | MMAL_COMPONENT_T *decoder; |
| 364 | int ret = 0; |
| 365 | |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 366 | ctx->pkt = avctx->internal->in_pkt; |
| 367 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 368 | bcm_host_init(); |
| 369 | |
wm4 | 49e531f | 2015-04-13 17:18:00 | [diff] [blame] | 370 | if (mmal_vc_init()) { |
| 371 | av_log(avctx, AV_LOG_ERROR, "Cannot initialize MMAL VC driver!\n"); |
| 372 | return AVERROR(ENOSYS); |
| 373 | } |
| 374 | |
Andreas Rheinhardt | 687a287 | 2024-02-05 18:58:20 | [diff] [blame] | 375 | if ((ret = ff_get_format(avctx, mmal_pixfmts)) < 0) |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 376 | return ret; |
| 377 | |
| 378 | avctx->pix_fmt = ret; |
| 379 | |
| 380 | if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &ctx->decoder))) |
| 381 | goto fail; |
| 382 | |
| 383 | decoder = ctx->decoder; |
| 384 | |
| 385 | format_in = decoder->input[0]->format; |
| 386 | format_in->type = MMAL_ES_TYPE_VIDEO; |
Julian Scheel | 7ebe12f | 2015-10-21 15:10:48 | [diff] [blame] | 387 | switch (avctx->codec_id) { |
Clément Bœsch | d16aefe | 2016-06-21 11:35:31 | [diff] [blame] | 388 | case AV_CODEC_ID_MPEG2VIDEO: |
| 389 | format_in->encoding = MMAL_ENCODING_MP2V; |
| 390 | break; |
| 391 | case AV_CODEC_ID_MPEG4: |
| 392 | format_in->encoding = MMAL_ENCODING_MP4V; |
| 393 | break; |
| 394 | case AV_CODEC_ID_VC1: |
| 395 | format_in->encoding = MMAL_ENCODING_WVC1; |
| 396 | break; |
| 397 | case AV_CODEC_ID_H264: |
| 398 | default: |
| 399 | format_in->encoding = MMAL_ENCODING_H264; |
| 400 | break; |
Julian Scheel | 7ebe12f | 2015-10-21 15:10:48 | [diff] [blame] | 401 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 402 | format_in->es->video.width = FFALIGN(avctx->width, 32); |
| 403 | format_in->es->video.height = FFALIGN(avctx->height, 16); |
| 404 | format_in->es->video.crop.width = avctx->width; |
| 405 | format_in->es->video.crop.height = avctx->height; |
| 406 | format_in->es->video.frame_rate.num = 24000; |
| 407 | format_in->es->video.frame_rate.den = 1001; |
| 408 | format_in->es->video.par.num = avctx->sample_aspect_ratio.num; |
| 409 | format_in->es->video.par.den = avctx->sample_aspect_ratio.den; |
| 410 | format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED; |
| 411 | |
Clément Bœsch | 67e370e | 2017-03-26 23:31:37 | [diff] [blame] | 412 | av_log(avctx, AV_LOG_DEBUG, "Using MMAL %s encoding.\n", |
| 413 | av_fourcc2str(format_in->encoding)); |
wm4 | d27a12c | 2016-01-28 16:23:46 | [diff] [blame] | 414 | |
wm4 | 74beead | 2016-04-14 10:21:26 | [diff] [blame] | 415 | #if HAVE_MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS |
wm4 | 14a90c9 | 2016-01-28 16:24:53 | [diff] [blame] | 416 | if (mmal_port_parameter_set_uint32(decoder->input[0], MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS, |
| 417 | -1 - ctx->extra_decoder_buffers)) { |
| 418 | av_log(avctx, AV_LOG_WARNING, "Could not set input buffering limit.\n"); |
| 419 | } |
wm4 | 74beead | 2016-04-14 10:21:26 | [diff] [blame] | 420 | #endif |
wm4 | 14a90c9 | 2016-01-28 16:24:53 | [diff] [blame] | 421 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 422 | if ((status = mmal_port_format_commit(decoder->input[0]))) |
| 423 | goto fail; |
| 424 | |
| 425 | decoder->input[0]->buffer_num = |
| 426 | FFMAX(decoder->input[0]->buffer_num_min, 20); |
| 427 | decoder->input[0]->buffer_size = |
| 428 | FFMAX(decoder->input[0]->buffer_size_min, 512 * 1024); |
| 429 | ctx->pool_in = mmal_pool_create(decoder->input[0]->buffer_num, 0); |
| 430 | if (!ctx->pool_in) { |
| 431 | ret = AVERROR(ENOMEM); |
| 432 | goto fail; |
| 433 | } |
| 434 | |
| 435 | if ((ret = ffmal_update_format(avctx)) < 0) |
| 436 | goto fail; |
| 437 | |
| 438 | ctx->queue_decoded_frames = mmal_queue_create(); |
| 439 | if (!ctx->queue_decoded_frames) |
| 440 | goto fail; |
| 441 | |
| 442 | decoder->input[0]->userdata = (void*)avctx; |
| 443 | decoder->output[0]->userdata = (void*)avctx; |
| 444 | decoder->control->userdata = (void*)avctx; |
| 445 | |
| 446 | if ((status = mmal_port_enable(decoder->control, control_port_cb))) |
| 447 | goto fail; |
| 448 | if ((status = mmal_port_enable(decoder->input[0], input_callback))) |
| 449 | goto fail; |
| 450 | if ((status = mmal_port_enable(decoder->output[0], output_callback))) |
| 451 | goto fail; |
| 452 | |
| 453 | if ((status = mmal_component_enable(decoder))) |
| 454 | goto fail; |
| 455 | |
| 456 | return 0; |
| 457 | |
| 458 | fail: |
| 459 | ffmmal_close_decoder(avctx); |
| 460 | return ret < 0 ? ret : AVERROR_UNKNOWN; |
| 461 | } |
| 462 | |
| 463 | static void ffmmal_flush(AVCodecContext *avctx) |
| 464 | { |
| 465 | MMALDecodeContext *ctx = avctx->priv_data; |
| 466 | MMAL_COMPONENT_T *decoder = ctx->decoder; |
| 467 | MMAL_STATUS_T status; |
| 468 | |
| 469 | ffmmal_stop_decoder(avctx); |
| 470 | |
| 471 | if ((status = mmal_port_enable(decoder->control, control_port_cb))) |
| 472 | goto fail; |
| 473 | if ((status = mmal_port_enable(decoder->input[0], input_callback))) |
| 474 | goto fail; |
| 475 | if ((status = mmal_port_enable(decoder->output[0], output_callback))) |
| 476 | goto fail; |
| 477 | |
| 478 | return; |
| 479 | |
| 480 | fail: |
| 481 | av_log(avctx, AV_LOG_ERROR, "MMAL flush error: %i\n", (int)status); |
| 482 | } |
| 483 | |
| 484 | // Split packets and add them to the waiting_buffers list. We don't queue them |
| 485 | // immediately, because it can happen that the decoder is temporarily blocked |
| 486 | // (due to us not reading/returning enough output buffers) and won't accept |
| 487 | // new input. (This wouldn't be an issue if MMAL input buffers always were |
| 488 | // complete frames - then the input buffer just would have to be big enough.) |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 489 | // If is_extradata is set, send it as MMAL_BUFFER_HEADER_FLAG_CONFIG. |
| 490 | static int ffmmal_add_packet(AVCodecContext *avctx, AVPacket *avpkt, |
| 491 | int is_extradata) |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 492 | { |
| 493 | MMALDecodeContext *ctx = avctx->priv_data; |
Andreas Rheinhardt | 9644ee2 | 2021-12-09 11:37:18 | [diff] [blame] | 494 | const AVBufferRef *buf = NULL; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 495 | int size = 0; |
| 496 | uint8_t *data = (uint8_t *)""; |
| 497 | uint8_t *start; |
| 498 | int ret = 0; |
| 499 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 500 | if (avpkt->size) { |
Andreas Rheinhardt | 9644ee2 | 2021-12-09 11:37:18 | [diff] [blame] | 501 | ret = av_packet_make_refcounted(avpkt); |
| 502 | if (ret < 0) |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 503 | goto done; |
Andreas Rheinhardt | 9644ee2 | 2021-12-09 11:37:18 | [diff] [blame] | 504 | buf = avpkt->buf; |
| 505 | data = avpkt->data; |
| 506 | size = avpkt->size; |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 507 | if (!is_extradata) |
| 508 | ctx->packets_sent++; |
wm4 | 67db57e | 2015-08-11 10:11:04 | [diff] [blame] | 509 | } else { |
wm4 | dafe4cd | 2015-11-10 12:47:01 | [diff] [blame] | 510 | if (ctx->eos_sent) |
| 511 | goto done; |
wm4 | 67db57e | 2015-08-11 10:11:04 | [diff] [blame] | 512 | if (!ctx->packets_sent) { |
| 513 | // Short-cut the flush logic to avoid upsetting MMAL. |
| 514 | ctx->eos_sent = 1; |
| 515 | ctx->eos_received = 1; |
| 516 | goto done; |
| 517 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 518 | } |
| 519 | |
| 520 | start = data; |
| 521 | |
| 522 | do { |
| 523 | FFBufferEntry *buffer = av_mallocz(sizeof(*buffer)); |
| 524 | if (!buffer) { |
| 525 | ret = AVERROR(ENOMEM); |
| 526 | goto done; |
| 527 | } |
| 528 | |
| 529 | buffer->data = data; |
| 530 | buffer->length = FFMIN(size, ctx->decoder->input[0]->buffer_size); |
| 531 | |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 532 | if (is_extradata) |
| 533 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG; |
| 534 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 535 | if (data == start) |
| 536 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_START; |
| 537 | |
| 538 | data += buffer->length; |
| 539 | size -= buffer->length; |
| 540 | |
| 541 | buffer->pts = avpkt->pts == AV_NOPTS_VALUE ? MMAL_TIME_UNKNOWN : avpkt->pts; |
| 542 | buffer->dts = avpkt->dts == AV_NOPTS_VALUE ? MMAL_TIME_UNKNOWN : avpkt->dts; |
| 543 | |
wm4 | 39f01e3 | 2015-09-23 18:27:25 | [diff] [blame] | 544 | if (!size) { |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 545 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 546 | atomic_fetch_add(&ctx->packets_buffered, 1); |
wm4 | 39f01e3 | 2015-09-23 18:27:25 | [diff] [blame] | 547 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 548 | |
| 549 | if (!buffer->length) { |
| 550 | buffer->flags |= MMAL_BUFFER_HEADER_FLAG_EOS; |
| 551 | ctx->eos_sent = 1; |
| 552 | } |
| 553 | |
| 554 | if (buf) { |
| 555 | buffer->ref = av_buffer_ref(buf); |
| 556 | if (!buffer->ref) { |
| 557 | av_free(buffer); |
| 558 | ret = AVERROR(ENOMEM); |
| 559 | goto done; |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | // Insert at end of the list |
| 564 | if (!ctx->waiting_buffers) |
| 565 | ctx->waiting_buffers = buffer; |
| 566 | if (ctx->waiting_buffers_tail) |
| 567 | ctx->waiting_buffers_tail->next = buffer; |
| 568 | ctx->waiting_buffers_tail = buffer; |
| 569 | } while (size); |
| 570 | |
| 571 | done: |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 572 | av_packet_unref(avpkt); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 573 | return ret; |
| 574 | } |
| 575 | |
| 576 | // Move prepared/split packets from waiting_buffers to the MMAL decoder. |
| 577 | static int ffmmal_fill_input_port(AVCodecContext *avctx) |
| 578 | { |
| 579 | MMALDecodeContext *ctx = avctx->priv_data; |
| 580 | |
| 581 | while (ctx->waiting_buffers) { |
| 582 | MMAL_BUFFER_HEADER_T *mbuffer; |
| 583 | FFBufferEntry *buffer; |
| 584 | MMAL_STATUS_T status; |
| 585 | |
| 586 | mbuffer = mmal_queue_get(ctx->pool_in->queue); |
| 587 | if (!mbuffer) |
| 588 | return 0; |
| 589 | |
| 590 | buffer = ctx->waiting_buffers; |
| 591 | |
| 592 | mmal_buffer_header_reset(mbuffer); |
| 593 | mbuffer->cmd = 0; |
| 594 | mbuffer->pts = buffer->pts; |
| 595 | mbuffer->dts = buffer->dts; |
| 596 | mbuffer->flags = buffer->flags; |
| 597 | mbuffer->data = buffer->data; |
| 598 | mbuffer->length = buffer->length; |
wm4 | 65db489 | 2015-09-23 18:27:24 | [diff] [blame] | 599 | mbuffer->user_data = buffer; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 600 | mbuffer->alloc_size = ctx->decoder->input[0]->buffer_size; |
| 601 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 602 | // Remove from start of the list |
| 603 | ctx->waiting_buffers = buffer->next; |
| 604 | if (ctx->waiting_buffers_tail == buffer) |
| 605 | ctx->waiting_buffers_tail = NULL; |
wm4 | 65db489 | 2015-09-23 18:27:24 | [diff] [blame] | 606 | |
| 607 | if ((status = mmal_port_send_buffer(ctx->decoder->input[0], mbuffer))) { |
| 608 | mmal_buffer_header_release(mbuffer); |
| 609 | av_buffer_unref(&buffer->ref); |
wm4 | 39f01e3 | 2015-09-23 18:27:25 | [diff] [blame] | 610 | if (buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 611 | atomic_fetch_add(&ctx->packets_buffered, -1); |
wm4 | 65db489 | 2015-09-23 18:27:24 | [diff] [blame] | 612 | av_free(buffer); |
| 613 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 614 | |
| 615 | if (status) { |
| 616 | av_log(avctx, AV_LOG_ERROR, "MMAL error %d when sending input\n", (int)status); |
| 617 | return AVERROR_UNKNOWN; |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | return 0; |
| 622 | } |
| 623 | |
| 624 | static int ffmal_copy_frame(AVCodecContext *avctx, AVFrame *frame, |
| 625 | MMAL_BUFFER_HEADER_T *buffer) |
| 626 | { |
| 627 | MMALDecodeContext *ctx = avctx->priv_data; |
| 628 | int ret = 0; |
| 629 | |
James Almer | 2f561ba | 2023-04-11 18:02:14 | [diff] [blame] | 630 | if (ctx->interlaced_frame) |
| 631 | frame->flags |= AV_FRAME_FLAG_INTERLACED; |
| 632 | if (ctx->top_field_first) |
| 633 | frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; |
Jens Ziller | bc7066f | 2016-08-14 14:44:39 | [diff] [blame] | 634 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 635 | if (avctx->pix_fmt == AV_PIX_FMT_MMAL) { |
| 636 | if (!ctx->pool_out) |
| 637 | return AVERROR_UNKNOWN; // format change code failed with OOM previously |
| 638 | |
| 639 | if ((ret = ff_decode_frame_props(avctx, frame)) < 0) |
| 640 | goto done; |
| 641 | |
| 642 | if ((ret = ffmmal_set_ref(frame, ctx->pool_out, buffer)) < 0) |
| 643 | goto done; |
| 644 | } else { |
| 645 | int w = FFALIGN(avctx->width, 32); |
| 646 | int h = FFALIGN(avctx->height, 16); |
Martin Storsjö | b8e899f | 2016-04-12 07:48:10 | [diff] [blame] | 647 | uint8_t *src[4]; |
| 648 | int linesize[4]; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 649 | |
| 650 | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
| 651 | goto done; |
| 652 | |
Martin Storsjö | b8e899f | 2016-04-12 07:48:10 | [diff] [blame] | 653 | av_image_fill_arrays(src, linesize, |
| 654 | buffer->data + buffer->type->video.offset[0], |
| 655 | avctx->pix_fmt, w, h, 1); |
Andreas Rheinhardt | 423b6a7 | 2023-09-06 22:09:10 | [diff] [blame] | 656 | av_image_copy2(frame->data, frame->linesize, src, linesize, |
| 657 | avctx->pix_fmt, avctx->width, avctx->height); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 658 | } |
| 659 | |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 660 | frame->sample_aspect_ratio = avctx->sample_aspect_ratio; |
| 661 | frame->width = avctx->width; |
| 662 | frame->width = avctx->width; |
| 663 | frame->height = avctx->height; |
| 664 | frame->format = avctx->pix_fmt; |
| 665 | |
Anton Khirnov | 32c8359 | 2016-03-19 20:45:24 | [diff] [blame] | 666 | frame->pts = buffer->pts == MMAL_TIME_UNKNOWN ? AV_NOPTS_VALUE : buffer->pts; |
wm4 | 9940459 | 2015-09-08 17:42:26 | [diff] [blame] | 667 | frame->pkt_dts = AV_NOPTS_VALUE; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 668 | |
| 669 | done: |
| 670 | return ret; |
| 671 | } |
| 672 | |
| 673 | // Fetch a decoded buffer and place it into the frame parameter. |
| 674 | static int ffmmal_read_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame) |
| 675 | { |
| 676 | MMALDecodeContext *ctx = avctx->priv_data; |
| 677 | MMAL_BUFFER_HEADER_T *buffer = NULL; |
| 678 | MMAL_STATUS_T status = 0; |
| 679 | int ret = 0; |
| 680 | |
| 681 | if (ctx->eos_received) |
| 682 | goto done; |
| 683 | |
| 684 | while (1) { |
| 685 | // To ensure decoding in lockstep with a constant delay between fed packets |
| 686 | // and output frames, we always wait until an output buffer is available. |
| 687 | // Except during start we don't know after how many input packets the decoder |
| 688 | // is going to return the first buffer, and we can't distinguish decoder |
| 689 | // being busy from decoder waiting for input. So just poll at the start and |
| 690 | // keep feeding new data to the buffer. |
| 691 | // We are pretty sure the decoder will produce output if we sent more input |
Clément Bœsch | d16aefe | 2016-06-21 11:35:31 | [diff] [blame] | 692 | // frames than what a H.264 decoder could logically delay. This avoids too |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 693 | // excessive buffering. |
| 694 | // We also wait if we sent eos, but didn't receive it yet (think of decoding |
| 695 | // stream with a very low number of frames). |
Anton Khirnov | 8385ba5 | 2016-07-17 20:24:35 | [diff] [blame] | 696 | if (atomic_load(&ctx->packets_buffered) > MAX_DELAYED_FRAMES || |
wm4 | 67db57e | 2015-08-11 10:11:04 | [diff] [blame] | 697 | (ctx->packets_sent && ctx->eos_sent)) { |
wm4 | 750f72d | 2015-08-11 09:26:19 | [diff] [blame] | 698 | // MMAL will ignore broken input packets, which means the frame we |
| 699 | // expect here may never arrive. Dealing with this correctly is |
| 700 | // complicated, so here's a hack to avoid that it freezes forever |
| 701 | // in this unlikely situation. |
| 702 | buffer = mmal_queue_timedwait(ctx->queue_decoded_frames, 100); |
| 703 | if (!buffer) { |
| 704 | av_log(avctx, AV_LOG_ERROR, "Did not get output frame from MMAL.\n"); |
| 705 | ret = AVERROR_UNKNOWN; |
| 706 | goto done; |
| 707 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 708 | } else { |
| 709 | buffer = mmal_queue_get(ctx->queue_decoded_frames); |
wm4 | 750f72d | 2015-08-11 09:26:19 | [diff] [blame] | 710 | if (!buffer) |
| 711 | goto done; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 712 | } |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 713 | |
| 714 | ctx->eos_received |= !!(buffer->flags & MMAL_BUFFER_HEADER_FLAG_EOS); |
| 715 | if (ctx->eos_received) |
| 716 | goto done; |
| 717 | |
| 718 | if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { |
| 719 | MMAL_COMPONENT_T *decoder = ctx->decoder; |
| 720 | MMAL_EVENT_FORMAT_CHANGED_T *ev = mmal_event_format_changed_get(buffer); |
| 721 | MMAL_BUFFER_HEADER_T *stale_buffer; |
| 722 | |
| 723 | av_log(avctx, AV_LOG_INFO, "Changing output format.\n"); |
| 724 | |
| 725 | if ((status = mmal_port_disable(decoder->output[0]))) |
| 726 | goto done; |
| 727 | |
| 728 | while ((stale_buffer = mmal_queue_get(ctx->queue_decoded_frames))) |
| 729 | mmal_buffer_header_release(stale_buffer); |
| 730 | |
| 731 | mmal_format_copy(decoder->output[0]->format, ev->format); |
| 732 | |
| 733 | if ((ret = ffmal_update_format(avctx)) < 0) |
| 734 | goto done; |
| 735 | |
| 736 | if ((status = mmal_port_enable(decoder->output[0], output_callback))) |
| 737 | goto done; |
| 738 | |
| 739 | if ((ret = ffmmal_fill_output_port(avctx)) < 0) |
| 740 | goto done; |
| 741 | |
| 742 | if ((ret = ffmmal_fill_input_port(avctx)) < 0) |
| 743 | goto done; |
| 744 | |
| 745 | mmal_buffer_header_release(buffer); |
| 746 | continue; |
| 747 | } else if (buffer->cmd) { |
Clément Bœsch | 67e370e | 2017-03-26 23:31:37 | [diff] [blame] | 748 | av_log(avctx, AV_LOG_WARNING, "Unknown MMAL event %s on output port\n", |
| 749 | av_fourcc2str(buffer->cmd)); |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 750 | goto done; |
| 751 | } else if (buffer->length == 0) { |
| 752 | // Unused output buffer that got drained after format change. |
| 753 | mmal_buffer_header_release(buffer); |
| 754 | continue; |
| 755 | } |
| 756 | |
| 757 | ctx->frames_output++; |
| 758 | |
| 759 | if ((ret = ffmal_copy_frame(avctx, frame, buffer)) < 0) |
| 760 | goto done; |
| 761 | |
| 762 | *got_frame = 1; |
| 763 | break; |
| 764 | } |
| 765 | |
| 766 | done: |
| 767 | if (buffer) |
| 768 | mmal_buffer_header_release(buffer); |
| 769 | if (status && ret >= 0) |
| 770 | ret = AVERROR_UNKNOWN; |
| 771 | return ret; |
| 772 | } |
| 773 | |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 774 | static int ffmmal_receive_frame(AVCodecContext *avctx, AVFrame *frame) |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 775 | { |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 776 | MMALDecodeContext *ctx = avctx->priv_data; |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 777 | AVPacket *const avpkt = ctx->pkt; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 778 | int ret = 0; |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 779 | int got_frame = 0; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 780 | |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 781 | if (avctx->extradata_size && !ctx->extradata_sent) { |
Ho Ming Shun | 6f0e8b9 | 2021-09-24 09:04:37 | [diff] [blame] | 782 | avpkt->data = avctx->extradata; |
| 783 | avpkt->size = avctx->extradata_size; |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 784 | ctx->extradata_sent = 1; |
Ho Ming Shun | 6f0e8b9 | 2021-09-24 09:04:37 | [diff] [blame] | 785 | if ((ret = ffmmal_add_packet(avctx, avpkt, 1)) < 0) |
wm4 | f290e48 | 2015-09-24 06:49:41 | [diff] [blame] | 786 | return ret; |
| 787 | } |
| 788 | |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 789 | ret = ff_decode_get_packet(avctx, avpkt); |
| 790 | if (ret == 0) { |
| 791 | if ((ret = ffmmal_add_packet(avctx, avpkt, 0)) < 0) |
| 792 | return ret; |
| 793 | } else if (ret < 0 && !(ret == AVERROR(EAGAIN))) |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 794 | return ret; |
| 795 | |
| 796 | if ((ret = ffmmal_fill_input_port(avctx)) < 0) |
| 797 | return ret; |
| 798 | |
| 799 | if ((ret = ffmmal_fill_output_port(avctx)) < 0) |
| 800 | return ret; |
| 801 | |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 802 | if ((ret = ffmmal_read_frame(avctx, frame, &got_frame)) < 0) |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 803 | return ret; |
| 804 | |
| 805 | // ffmmal_read_frame() can block for a while. Since the decoder is |
| 806 | // asynchronous, it's a good idea to fill the ports again. |
| 807 | |
| 808 | if ((ret = ffmmal_fill_output_port(avctx)) < 0) |
| 809 | return ret; |
| 810 | |
| 811 | if ((ret = ffmmal_fill_input_port(avctx)) < 0) |
| 812 | return ret; |
| 813 | |
Ho Ming Shun | b54377b | 2021-09-24 09:04:35 | [diff] [blame] | 814 | if (!got_frame && ret == 0) |
| 815 | return AVERROR(EAGAIN); |
| 816 | else |
| 817 | return ret; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 818 | } |
| 819 | |
Mark Thompson | cd32279 | 2020-11-08 18:51:15 | [diff] [blame] | 820 | static const AVCodecHWConfigInternal *const mmal_hw_configs[] = { |
Mark Thompson | 758fbc5 | 2017-10-25 23:18:40 | [diff] [blame] | 821 | HW_CONFIG_INTERNAL(MMAL), |
| 822 | NULL |
| 823 | }; |
| 824 | |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 825 | static const AVOption options[]={ |
| 826 | {"extra_buffers", "extra buffers", offsetof(MMALDecodeContext, extra_buffers), AV_OPT_TYPE_INT, {.i64 = 10}, 0, 256, 0}, |
wm4 | 14a90c9 | 2016-01-28 16:24:53 | [diff] [blame] | 827 | {"extra_decoder_buffers", "extra MMAL internal buffered frames", offsetof(MMALDecodeContext, extra_decoder_buffers), AV_OPT_TYPE_INT, {.i64 = 10}, 0, 256, 0}, |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 828 | {NULL} |
| 829 | }; |
| 830 | |
Andreas Rheinhardt | 7fe5c7f | 2021-12-09 11:58:02 | [diff] [blame] | 831 | static const AVClass ffmmal_dec_class = { |
| 832 | .class_name = "mmal_dec", |
Anton Khirnov | 08bebeb | 2024-01-19 12:33:28 | [diff] [blame] | 833 | .item_name = av_default_item_name, |
Andreas Rheinhardt | 7fe5c7f | 2021-12-09 11:58:02 | [diff] [blame] | 834 | .option = options, |
| 835 | .version = LIBAVUTIL_VERSION_INT, |
| 836 | }; |
wm4 | c484561 | 2015-03-27 22:04:08 | [diff] [blame] | 837 | |
Julian Scheel | 7ebe12f | 2015-10-21 15:10:48 | [diff] [blame] | 838 | #define FFMMAL_DEC(NAME, ID) \ |
Andreas Rheinhardt | 20f9727 | 2022-03-16 20:09:54 | [diff] [blame] | 839 | const FFCodec ff_##NAME##_mmal_decoder = { \ |
| 840 | .p.name = #NAME "_mmal", \ |
Andreas Rheinhardt | 48286d4 | 2022-08-29 11:38:02 | [diff] [blame] | 841 | CODEC_LONG_NAME(#NAME " (mmal)"), \ |
Andreas Rheinhardt | 20f9727 | 2022-03-16 20:09:54 | [diff] [blame] | 842 | .p.type = AVMEDIA_TYPE_VIDEO, \ |
| 843 | .p.id = ID, \ |
Julian Scheel | 7ebe12f | 2015-10-21 15:10:48 | [diff] [blame] | 844 | .priv_data_size = sizeof(MMALDecodeContext), \ |
| 845 | .init = ffmmal_init_decoder, \ |
| 846 | .close = ffmmal_close_decoder, \ |
Andreas Rheinhardt | 4243da4 | 2022-03-30 21:28:24 | [diff] [blame] | 847 | FF_CODEC_RECEIVE_FRAME_CB(ffmmal_receive_frame), \ |
Julian Scheel | 7ebe12f | 2015-10-21 15:10:48 | [diff] [blame] | 848 | .flush = ffmmal_flush, \ |
Andreas Rheinhardt | 20f9727 | 2022-03-16 20:09:54 | [diff] [blame] | 849 | .p.priv_class = &ffmmal_dec_class, \ |
| 850 | .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, \ |
Zhao Zhili | e8a49b1 | 2023-11-20 20:05:08 | [diff] [blame] | 851 | .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, \ |
Mark Thompson | 758fbc5 | 2017-10-25 23:18:40 | [diff] [blame] | 852 | .hw_configs = mmal_hw_configs, \ |
Andreas Rheinhardt | 20f9727 | 2022-03-16 20:09:54 | [diff] [blame] | 853 | .p.wrapper_name = "mmal", \ |
Julian Scheel | 7ebe12f | 2015-10-21 15:10:48 | [diff] [blame] | 854 | }; |
| 855 | |
| 856 | FFMMAL_DEC(h264, AV_CODEC_ID_H264) |
| 857 | FFMMAL_DEC(mpeg2, AV_CODEC_ID_MPEG2VIDEO) |
wm4 | 7b1b53f | 2016-01-28 16:24:40 | [diff] [blame] | 858 | FFMMAL_DEC(mpeg4, AV_CODEC_ID_MPEG4) |
wm4 | b07cbf6 | 2015-11-06 11:59:16 | [diff] [blame] | 859 | FFMMAL_DEC(vc1, AV_CODEC_ID_VC1) |