1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * V4L2 mem2mem decoders 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org> 5cabdff1aSopenharmony_ci * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org> 6cabdff1aSopenharmony_ci * 7cabdff1aSopenharmony_ci * This file is part of FFmpeg. 8cabdff1aSopenharmony_ci * 9cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 10cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 11cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 12cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 13cabdff1aSopenharmony_ci * 14cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 15cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 16cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17cabdff1aSopenharmony_ci * Lesser General Public License for more details. 18cabdff1aSopenharmony_ci * 19cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 20cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 21cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22cabdff1aSopenharmony_ci */ 23cabdff1aSopenharmony_ci 24cabdff1aSopenharmony_ci#include <linux/videodev2.h> 25cabdff1aSopenharmony_ci#include <sys/ioctl.h> 26cabdff1aSopenharmony_ci#include "libavutil/pixfmt.h" 27cabdff1aSopenharmony_ci#include "libavutil/pixdesc.h" 28cabdff1aSopenharmony_ci#include "libavutil/opt.h" 29cabdff1aSopenharmony_ci#include "libavcodec/avcodec.h" 30cabdff1aSopenharmony_ci#include "codec_internal.h" 31cabdff1aSopenharmony_ci#include "libavcodec/decode.h" 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_ci#include "v4l2_context.h" 34cabdff1aSopenharmony_ci#include "v4l2_m2m.h" 35cabdff1aSopenharmony_ci#include "v4l2_fmt.h" 36cabdff1aSopenharmony_ci 37cabdff1aSopenharmony_cistatic int v4l2_try_start(AVCodecContext *avctx) 38cabdff1aSopenharmony_ci{ 39cabdff1aSopenharmony_ci V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; 40cabdff1aSopenharmony_ci V4L2Context *const capture = &s->capture; 41cabdff1aSopenharmony_ci V4L2Context *const output = &s->output; 42cabdff1aSopenharmony_ci struct v4l2_selection selection = { 0 }; 43cabdff1aSopenharmony_ci int ret; 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_ci /* 1. start the output process */ 46cabdff1aSopenharmony_ci if (!output->streamon) { 47cabdff1aSopenharmony_ci ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON); 48cabdff1aSopenharmony_ci if (ret < 0) { 49cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output context\n"); 50cabdff1aSopenharmony_ci return ret; 51cabdff1aSopenharmony_ci } 52cabdff1aSopenharmony_ci } 53cabdff1aSopenharmony_ci 54cabdff1aSopenharmony_ci if (capture->streamon) 55cabdff1aSopenharmony_ci return 0; 56cabdff1aSopenharmony_ci 57cabdff1aSopenharmony_ci /* 2. get the capture format */ 58cabdff1aSopenharmony_ci capture->format.type = capture->type; 59cabdff1aSopenharmony_ci ret = ioctl(s->fd, VIDIOC_G_FMT, &capture->format); 60cabdff1aSopenharmony_ci if (ret) { 61cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "VIDIOC_G_FMT ioctl\n"); 62cabdff1aSopenharmony_ci return ret; 63cabdff1aSopenharmony_ci } 64cabdff1aSopenharmony_ci 65cabdff1aSopenharmony_ci /* 2.1 update the AVCodecContext */ 66cabdff1aSopenharmony_ci avctx->pix_fmt = ff_v4l2_format_v4l2_to_avfmt(capture->format.fmt.pix_mp.pixelformat, AV_CODEC_ID_RAWVIDEO); 67cabdff1aSopenharmony_ci capture->av_pix_fmt = avctx->pix_fmt; 68cabdff1aSopenharmony_ci 69cabdff1aSopenharmony_ci /* 3. set the crop parameters */ 70cabdff1aSopenharmony_ci selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 71cabdff1aSopenharmony_ci selection.r.height = avctx->coded_height; 72cabdff1aSopenharmony_ci selection.r.width = avctx->coded_width; 73cabdff1aSopenharmony_ci ret = ioctl(s->fd, VIDIOC_S_SELECTION, &selection); 74cabdff1aSopenharmony_ci if (!ret) { 75cabdff1aSopenharmony_ci ret = ioctl(s->fd, VIDIOC_G_SELECTION, &selection); 76cabdff1aSopenharmony_ci if (ret) { 77cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_WARNING, "VIDIOC_G_SELECTION ioctl\n"); 78cabdff1aSopenharmony_ci } else { 79cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_DEBUG, "crop output %dx%d\n", selection.r.width, selection.r.height); 80cabdff1aSopenharmony_ci /* update the size of the resulting frame */ 81cabdff1aSopenharmony_ci capture->height = selection.r.height; 82cabdff1aSopenharmony_ci capture->width = selection.r.width; 83cabdff1aSopenharmony_ci } 84cabdff1aSopenharmony_ci } 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_ci /* 4. init the capture context now that we have the capture format */ 87cabdff1aSopenharmony_ci if (!capture->buffers) { 88cabdff1aSopenharmony_ci ret = ff_v4l2_context_init(capture); 89cabdff1aSopenharmony_ci if (ret) { 90cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "can't request capture buffers\n"); 91cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 92cabdff1aSopenharmony_ci } 93cabdff1aSopenharmony_ci } 94cabdff1aSopenharmony_ci 95cabdff1aSopenharmony_ci /* 5. start the capture process */ 96cabdff1aSopenharmony_ci ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON); 97cabdff1aSopenharmony_ci if (ret) { 98cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON, on capture context\n"); 99cabdff1aSopenharmony_ci return ret; 100cabdff1aSopenharmony_ci } 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci return 0; 103cabdff1aSopenharmony_ci} 104cabdff1aSopenharmony_ci 105cabdff1aSopenharmony_cistatic int v4l2_prepare_decoder(V4L2m2mContext *s) 106cabdff1aSopenharmony_ci{ 107cabdff1aSopenharmony_ci struct v4l2_event_subscription sub; 108cabdff1aSopenharmony_ci V4L2Context *output = &s->output; 109cabdff1aSopenharmony_ci int ret; 110cabdff1aSopenharmony_ci 111cabdff1aSopenharmony_ci /** 112cabdff1aSopenharmony_ci * requirements 113cabdff1aSopenharmony_ci */ 114cabdff1aSopenharmony_ci memset(&sub, 0, sizeof(sub)); 115cabdff1aSopenharmony_ci sub.type = V4L2_EVENT_SOURCE_CHANGE; 116cabdff1aSopenharmony_ci ret = ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); 117cabdff1aSopenharmony_ci if ( ret < 0) { 118cabdff1aSopenharmony_ci if (output->height == 0 || output->width == 0) { 119cabdff1aSopenharmony_ci av_log(s->avctx, AV_LOG_ERROR, 120cabdff1aSopenharmony_ci "the v4l2 driver does not support VIDIOC_SUBSCRIBE_EVENT\n" 121cabdff1aSopenharmony_ci "you must provide codec_height and codec_width on input\n"); 122cabdff1aSopenharmony_ci return ret; 123cabdff1aSopenharmony_ci } 124cabdff1aSopenharmony_ci } 125cabdff1aSopenharmony_ci 126cabdff1aSopenharmony_ci memset(&sub, 0, sizeof(sub)); 127cabdff1aSopenharmony_ci sub.type = V4L2_EVENT_EOS; 128cabdff1aSopenharmony_ci ret = ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub); 129cabdff1aSopenharmony_ci if (ret < 0) 130cabdff1aSopenharmony_ci av_log(s->avctx, AV_LOG_WARNING, 131cabdff1aSopenharmony_ci "the v4l2 driver does not support end of stream VIDIOC_SUBSCRIBE_EVENT\n"); 132cabdff1aSopenharmony_ci 133cabdff1aSopenharmony_ci return 0; 134cabdff1aSopenharmony_ci} 135cabdff1aSopenharmony_ci 136cabdff1aSopenharmony_cistatic int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame) 137cabdff1aSopenharmony_ci{ 138cabdff1aSopenharmony_ci V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; 139cabdff1aSopenharmony_ci V4L2Context *const capture = &s->capture; 140cabdff1aSopenharmony_ci V4L2Context *const output = &s->output; 141cabdff1aSopenharmony_ci int ret; 142cabdff1aSopenharmony_ci 143cabdff1aSopenharmony_ci if (!s->buf_pkt.size) { 144cabdff1aSopenharmony_ci ret = ff_decode_get_packet(avctx, &s->buf_pkt); 145cabdff1aSopenharmony_ci if (ret < 0) { 146cabdff1aSopenharmony_ci if (ret == AVERROR(EAGAIN)) 147cabdff1aSopenharmony_ci return ff_v4l2_context_dequeue_frame(capture, frame, 0); 148cabdff1aSopenharmony_ci else if (ret != AVERROR_EOF) 149cabdff1aSopenharmony_ci return ret; 150cabdff1aSopenharmony_ci } 151cabdff1aSopenharmony_ci } 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci if (s->draining) 154cabdff1aSopenharmony_ci goto dequeue; 155cabdff1aSopenharmony_ci 156cabdff1aSopenharmony_ci ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt); 157cabdff1aSopenharmony_ci if (ret < 0 && ret != AVERROR(EAGAIN)) 158cabdff1aSopenharmony_ci goto fail; 159cabdff1aSopenharmony_ci 160cabdff1aSopenharmony_ci /* if EAGAIN don't unref packet and try to enqueue in the next iteration */ 161cabdff1aSopenharmony_ci if (ret != AVERROR(EAGAIN)) 162cabdff1aSopenharmony_ci av_packet_unref(&s->buf_pkt); 163cabdff1aSopenharmony_ci 164cabdff1aSopenharmony_ci if (!s->draining) { 165cabdff1aSopenharmony_ci ret = v4l2_try_start(avctx); 166cabdff1aSopenharmony_ci if (ret) { 167cabdff1aSopenharmony_ci /* cant recover */ 168cabdff1aSopenharmony_ci if (ret != AVERROR(ENOMEM)) 169cabdff1aSopenharmony_ci ret = 0; 170cabdff1aSopenharmony_ci goto fail; 171cabdff1aSopenharmony_ci } 172cabdff1aSopenharmony_ci } 173cabdff1aSopenharmony_ci 174cabdff1aSopenharmony_cidequeue: 175cabdff1aSopenharmony_ci return ff_v4l2_context_dequeue_frame(capture, frame, -1); 176cabdff1aSopenharmony_cifail: 177cabdff1aSopenharmony_ci av_packet_unref(&s->buf_pkt); 178cabdff1aSopenharmony_ci return ret; 179cabdff1aSopenharmony_ci} 180cabdff1aSopenharmony_ci 181cabdff1aSopenharmony_cistatic av_cold int v4l2_decode_init(AVCodecContext *avctx) 182cabdff1aSopenharmony_ci{ 183cabdff1aSopenharmony_ci V4L2Context *capture, *output; 184cabdff1aSopenharmony_ci V4L2m2mContext *s; 185cabdff1aSopenharmony_ci V4L2m2mPriv *priv = avctx->priv_data; 186cabdff1aSopenharmony_ci int ret; 187cabdff1aSopenharmony_ci 188cabdff1aSopenharmony_ci ret = ff_v4l2_m2m_create_context(priv, &s); 189cabdff1aSopenharmony_ci if (ret < 0) 190cabdff1aSopenharmony_ci return ret; 191cabdff1aSopenharmony_ci 192cabdff1aSopenharmony_ci capture = &s->capture; 193cabdff1aSopenharmony_ci output = &s->output; 194cabdff1aSopenharmony_ci 195cabdff1aSopenharmony_ci /* if these dimensions are invalid (ie, 0 or too small) an event will be raised 196cabdff1aSopenharmony_ci * by the v4l2 driver; this event will trigger a full pipeline reconfig and 197cabdff1aSopenharmony_ci * the proper values will be retrieved from the kernel driver. 198cabdff1aSopenharmony_ci */ 199cabdff1aSopenharmony_ci output->height = capture->height = avctx->coded_height; 200cabdff1aSopenharmony_ci output->width = capture->width = avctx->coded_width; 201cabdff1aSopenharmony_ci 202cabdff1aSopenharmony_ci output->av_codec_id = avctx->codec_id; 203cabdff1aSopenharmony_ci output->av_pix_fmt = AV_PIX_FMT_NONE; 204cabdff1aSopenharmony_ci 205cabdff1aSopenharmony_ci capture->av_codec_id = AV_CODEC_ID_RAWVIDEO; 206cabdff1aSopenharmony_ci capture->av_pix_fmt = avctx->pix_fmt; 207cabdff1aSopenharmony_ci 208cabdff1aSopenharmony_ci s->avctx = avctx; 209cabdff1aSopenharmony_ci ret = ff_v4l2_m2m_codec_init(priv); 210cabdff1aSopenharmony_ci if (ret) { 211cabdff1aSopenharmony_ci av_log(avctx, AV_LOG_ERROR, "can't configure decoder\n"); 212cabdff1aSopenharmony_ci return ret; 213cabdff1aSopenharmony_ci } 214cabdff1aSopenharmony_ci 215cabdff1aSopenharmony_ci return v4l2_prepare_decoder(s); 216cabdff1aSopenharmony_ci} 217cabdff1aSopenharmony_ci 218cabdff1aSopenharmony_cistatic av_cold int v4l2_decode_close(AVCodecContext *avctx) 219cabdff1aSopenharmony_ci{ 220cabdff1aSopenharmony_ci return ff_v4l2_m2m_codec_end(avctx->priv_data); 221cabdff1aSopenharmony_ci} 222cabdff1aSopenharmony_ci 223cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(V4L2m2mPriv, x) 224cabdff1aSopenharmony_ci#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM 225cabdff1aSopenharmony_ci 226cabdff1aSopenharmony_cistatic const AVOption options[] = { 227cabdff1aSopenharmony_ci V4L_M2M_DEFAULT_OPTS, 228cabdff1aSopenharmony_ci { "num_capture_buffers", "Number of buffers in the capture context", 229cabdff1aSopenharmony_ci OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 20, INT_MAX, FLAGS }, 230cabdff1aSopenharmony_ci { NULL}, 231cabdff1aSopenharmony_ci}; 232cabdff1aSopenharmony_ci 233cabdff1aSopenharmony_ci#define M2MDEC_CLASS(NAME) \ 234cabdff1aSopenharmony_ci static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \ 235cabdff1aSopenharmony_ci .class_name = #NAME "_v4l2m2m_decoder", \ 236cabdff1aSopenharmony_ci .item_name = av_default_item_name, \ 237cabdff1aSopenharmony_ci .option = options, \ 238cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, \ 239cabdff1aSopenharmony_ci }; 240cabdff1aSopenharmony_ci 241cabdff1aSopenharmony_ci#define M2MDEC(NAME, LONGNAME, CODEC, bsf_name) \ 242cabdff1aSopenharmony_ci M2MDEC_CLASS(NAME) \ 243cabdff1aSopenharmony_ci const FFCodec ff_ ## NAME ## _v4l2m2m_decoder = { \ 244cabdff1aSopenharmony_ci .p.name = #NAME "_v4l2m2m" , \ 245cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("V4L2 mem2mem " LONGNAME " decoder wrapper"), \ 246cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, \ 247cabdff1aSopenharmony_ci .p.id = CODEC , \ 248cabdff1aSopenharmony_ci .priv_data_size = sizeof(V4L2m2mPriv), \ 249cabdff1aSopenharmony_ci .p.priv_class = &v4l2_m2m_ ## NAME ## _dec_class, \ 250cabdff1aSopenharmony_ci .init = v4l2_decode_init, \ 251cabdff1aSopenharmony_ci FF_CODEC_RECEIVE_FRAME_CB(v4l2_receive_frame), \ 252cabdff1aSopenharmony_ci .close = v4l2_decode_close, \ 253cabdff1aSopenharmony_ci .bsfs = bsf_name, \ 254cabdff1aSopenharmony_ci .p.capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ 255cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \ 256cabdff1aSopenharmony_ci .p.wrapper_name = "v4l2m2m", \ 257cabdff1aSopenharmony_ci } 258cabdff1aSopenharmony_ci 259cabdff1aSopenharmony_ciM2MDEC(h264, "H.264", AV_CODEC_ID_H264, "h264_mp4toannexb"); 260cabdff1aSopenharmony_ciM2MDEC(hevc, "HEVC", AV_CODEC_ID_HEVC, "hevc_mp4toannexb"); 261cabdff1aSopenharmony_ciM2MDEC(mpeg1, "MPEG1", AV_CODEC_ID_MPEG1VIDEO, NULL); 262cabdff1aSopenharmony_ciM2MDEC(mpeg2, "MPEG2", AV_CODEC_ID_MPEG2VIDEO, NULL); 263cabdff1aSopenharmony_ciM2MDEC(mpeg4, "MPEG4", AV_CODEC_ID_MPEG4, NULL); 264cabdff1aSopenharmony_ciM2MDEC(h263, "H.263", AV_CODEC_ID_H263, NULL); 265cabdff1aSopenharmony_ciM2MDEC(vc1 , "VC1", AV_CODEC_ID_VC1, NULL); 266cabdff1aSopenharmony_ciM2MDEC(vp8, "VP8", AV_CODEC_ID_VP8, NULL); 267cabdff1aSopenharmony_ciM2MDEC(vp9, "VP9", AV_CODEC_ID_VP9, NULL); 268