From patchwork Thu Jun 15 11:47:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dawid Kozinski/Multimedia \\(PLT\\) /SRPOL/Staff Engineer/Samsung Electronics" X-Patchwork-Id: 42106 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:c526:b0:117:ac03:c9de with SMTP id gm38csp710546pzb; Thu, 15 Jun 2023 04:47:46 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7rTJsXCZoMF2XuAoM58qKN9X9WPU+kJH6N9GBCVr0KEuOOpG5iG+4UgZ4W8N4NDO+czfNE X-Received: by 2002:a17:907:1c85:b0:982:84a4:9f80 with SMTP id nb5-20020a1709071c8500b0098284a49f80mr4154681ejc.31.1686829665955; Thu, 15 Jun 2023 04:47:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686829665; cv=none; d=google.com; s=arc-20160816; b=K19KQ81P9moAJGirfVMKvoUyIupG1GJZUOnb1GewKblaiyP+6wz61mIuf2m8ZKLn57 AnEgTrMHQIYKZV3g+ngqdfL1M72XA3Va/U9D/6AmSX4uch8tmnVtLciZfv+Ww7joaxoB Kycwie1F/nNvKDcxKME37HPiSAKzHi5UJYpRuc+qdfyGGqz/ZknlFyLshFw7k1/WULZM 8aPwfXV8OOsAMXFkh/dmUxO52z3Q71RVQb1czRt7LIwpkd/FpN2rS6fspm2G/7TODSQX /RSiKavpHk32LBpRNWPuxY+xwqorCEbl5btjy8eVbD4Dm2mYWVXEg84Eh4MXYuqHD0U/ 0nGg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:cms-type:mime-version :message-id:date:to:from:dkim-signature:dkim-filter:delivered-to; bh=SqXA+YN75RcPiEh3xAipJYJpmlU3c2hMeZgRWzglO+g=; b=vtEjS7AgM3DUkTkPaTzLYn8bOeg6Vk4khFlnSiMmDDuB5vXWUELnGG0FF8Kj9UpfIy EWTEDPF6SdHga7uRltyIxlDAcL2G//8Dklf672BJbOSrc3cz6aUpTu0A8asXBJw4vchI t3SUHzIcKgJyzg1oanbFMKb+PyQXXC0ZtGOcW/v5E8JD5HoAVcQvCShh9r7rD3o0YZr/ Ek5mvaaQwFdUrRF6xUh+w7faz+epMeePtkDknPo4XZTRTi0JykysZDAr29pSynhGEzUB 5iHNacNQJsYmfiEyxZOEtGBr6e5DLQtzVD6mkWENEv+6aXJ/jSwXrKQQtMaEOF1PI/Xn Ty3Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@samsung.com header.s=mail20170921 header.b=lt6L5TQE; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=samsung.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id b22-20020aa7cd16000000b005169f7525desi726262edw.247.2023.06.15.04.47.45; Thu, 15 Jun 2023 04:47:45 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@samsung.com header.s=mail20170921 header.b=lt6L5TQE; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=samsung.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 240AE68C2DE; Thu, 15 Jun 2023 14:47:43 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mailout2.w1.samsung.com (mailout2.w1.samsung.com [210.118.77.12]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id B48C968C351 for ; Thu, 15 Jun 2023 14:47:35 +0300 (EEST) Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20230615114735euoutp026262ecac34c78b5f5eb0c624367edd1e~o0qJb5hCc2955329553euoutp02N for ; Thu, 15 Jun 2023 11:47:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20230615114735euoutp026262ecac34c78b5f5eb0c624367edd1e~o0qJb5hCc2955329553euoutp02N DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1686829655; bh=5IM9CbwBR+g2r1wBBaDdR2Fi+akuGJJjQNS7JGoxyyg=; h=From:To:Cc:Subject:Date:References:From; b=lt6L5TQEXtgbHxJIQCcs5zTqHT98Rn2n0nPzIAg48dMbU1G2meRtjMbs/w4sALA3Z GGinTK9rX+HShOrxchgwEquom/344yx6dteLsyKHVMf9JXtKacsbpYycOzeNO8gWgz hX3exNzY/uEZ/ILC8BXhtUg8z1mezdhL8Gpu89oU= Received: from eusmges2new.samsung.com (unknown [203.254.199.244]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20230615114735eucas1p1928f50d7448351d9464318491a05eb87~o0qJPnDO31388613886eucas1p1H; Thu, 15 Jun 2023 11:47:35 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges2new.samsung.com (EUCPMTA) with SMTP id 65.B0.11320.65AFA846; Thu, 15 Jun 2023 12:47:34 +0100 (BST) Received: from eusmtrp1.samsung.com (unknown [182.198.249.138]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20230615114734eucas1p2cc230a734b1b18652accf2d738e4cdd9~o0qI6hVkr1209712097eucas1p2u; Thu, 15 Jun 2023 11:47:34 +0000 (GMT) Received: from eusmgms1.samsung.com (unknown [182.198.249.179]) by eusmtrp1.samsung.com (KnoxPortal) with ESMTP id 20230615114734eusmtrp1daf2765b8e387b91345c55ffc6664219~o0qI59UxF1531015310eusmtrp1D; Thu, 15 Jun 2023 11:47:34 +0000 (GMT) X-AuditID: cbfec7f4-993ff70000022c38-a8-648afa56872a Received: from eusmtip1.samsung.com ( [203.254.199.221]) by eusmgms1.samsung.com (EUCPMTA) with SMTP id E8.7C.10549.65AFA846; Thu, 15 Jun 2023 12:47:34 +0100 (BST) Received: from AMDN5164.EU.corp.samsungelectronics.net (unknown [106.120.40.55]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20230615114734eusmtip163fff140308e5f05201ce525ef1cbaac~o0qIiaDGo3202032020eusmtip1B; Thu, 15 Jun 2023 11:47:34 +0000 (GMT) From: Dawid Kozinski To: d.frankiewic@samsung.com, ffmpeg-devel@ffmpeg.org Date: Thu, 15 Jun 2023 13:47:26 +0200 Message-Id: <20230615114726.615-1-d.kozinski@samsung.com> X-Mailer: git-send-email 2.37.3.windows.1 MIME-Version: 1.0 X-Unsent: 1 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrJIsWRmVeSWpSXmKPExsWy7djPc7phv7pSDA41mVt8ufaF2WLlt98s Ft8+nWF2YPb4s2gzi0ffllWMAUxRXDYpqTmZZalF+nYJXBnnn85gK9jWwljxoS+igfF1chcj J4eEgInEt74uti5GLg4hgRWMEnPnN7FDOF8YJe7euMsI4XxmlLjdsJQNpuXRgqlMEInljBJn t22HclqZJJZ3HWUEqWIT0JV4+eERE4gtImAqcbJ5L1g3s4C2xLq738FsYYEEiaMLfjGD2CwC qhJndn1nB7F5BSwlpry7xgixTVNiwpY5TBBxQYmTM5+wQMS5JdbNusQKMVNeonnrbGaQIyQE frJLvJnUCDSIA8hxkTi+0gqiXlji1fEt7BC2jMTpyT0sECXFEof6HSDMGolDP9IhKqwl3jYe ZwQJMwNdsH6XPkSFo8SXD/4QJp/EjbeCEOv5JCZtm84MEeaV6GgTgjBVJPo6xSDGSUk8XTYH qsJD4sov7QmMirOQvDQLyRuzEJYuYGRexSieWlqcm55abJSXWq5XnJhbXJqXrpecn7uJEZga Tv87/mUH4/JXH/UOMTJxMB5ilOBgVhLhXXaiK0WINyWxsiq1KD++qDQntfgQozQHi5I4r7bt yWQhgfTEktTs1NSC1CKYLBMHp1QD09SNXcm3z82Yef6o4qHDHzIzpR7ZXczfyCPSa878483V Taetrgk/eiLb42ZjNrGufOKR1Pf7Cuw7r+XYSYqsaVf4Uu/w9kHVVP9d6d5LFBRmnjr7LePT 3X9reOsZzPQ7NgmYKG6sDb56p87U+Gl9+L8bbtOl3gZYuu7KX9VarWz38NTWgKafoi+vJvKa qK7fWr/Gt0xeZfq2/RfuxV0rfJX88DR/Wq1CNWO+CoOfTLbc4hOKjidv33/Al2S7LiRPfZ2q /sm6y1lVjuzXtiVZ+zEd65Lmu6dS7u9v85BH7qADl8zS2Ur/o35vmWpoKrfQ4sPCqxMCu1Qe XT8T87vWLk7mdPT+rjVqrmpSHW9PKLEUZyQaajEXFScCAD0c6GJ8AwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFrrELMWRmVeSWpSXmKPExsVy+t/xu7phv7pSDO79lbf4cu0Ls8XKb79Z LL59OsPswOzxZ9FmFo++LasYA5ii9GyK8ktLUhUy8otLbJWiDS2M9AwtLfSMTCz1DI3NY62M TJX07WxSUnMyy1KL9O0S9DLOP53BVrCthbHiQ19EA+Pr5C5GTg4JAROJRwumMnUxcnEICSxl lJj46x0bREJKYunSRYwQtrDEn2tdbBBFzUwS7SdPMIMk2AR0JV5+eMQEYosImEtcXHcLrIFZ QFti3d3vYIOEBeIkJh04C2azCKhKnNn1nR3E5hWwlJjy7hrUAk2JCVvmMEHEBSVOznzCAhHn llg36xIrxEx5ieats5knMPLPQlI2C0lqASPTKkaR1NLi3PTcYkO94sTc4tK8dL3k/NxNjMDA 3Xbs5+YdjPNefdQ7xMjEwXiIUYKDWUmEd9mJrhQh3pTEyqrUovz4otKc1OJDjKZAt05klhJN zgfGTl5JvKGZgamhiZmlgamlmbGSOK9nQUeikEB6YklqdmpqQWoRTB8TB6dUA1Ot7KNo/xc/ +IxSYv+sftw774PGVJ9vOw8ckL1vFT73c+JC3p4tU0ULu4yWCNz9lVTItf7zxzmMQvGtM9Rf L89kWtp2z7xnh9WtFXkZ5lcPTHe2c98l1/nQJ1KAKd5wz6rTR5f92f7bdPXvqZlbTr5dvLB9 l+7muXbr0q9zP6y+YbDutcA070NOyx8cucn7zl/Ye2lGzM0P16NCep7+ks4Sarj6qT8wzHO1 5gzld36pbqWTvv5m+CDR7LNKXSheg/3rEo5Dgl0dW2pfrJz7pXOd4RmO7rZecaeTyYcqr1+W fPPu09rtWuektDnf1sxbu4ZJ0afkWJ3LGt15ppfOn9Qr/cC9KPJ1UoNA1nWlhqPlSizFGYmG WsxFxYkA4VvI9eUCAAA= X-CMS-MailID: 20230615114734eucas1p2cc230a734b1b18652accf2d738e4cdd9 X-Msg-Generator: CA X-RootMTR: 20230615114734eucas1p2cc230a734b1b18652accf2d738e4cdd9 X-EPHeader: CA CMS-TYPE: 201P X-CMS-RootMailID: 20230615114734eucas1p2cc230a734b1b18652accf2d738e4cdd9 References: Subject: [FFmpeg-devel] [PATCH v26 3/9] avformat/evc_demuxer: Added demuxer to handle reading EVC video files X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Dawid Kozinski Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: jw59BgRb9QPm - Provided AVInputFormat struct describing EVC input format (ff_evc_demuxer) Signed-off-by: Dawid Kozinski --- libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 5 +- libavcodec/evc_frame_merge_bsf.c | 170 +++++++++++++++++++ libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/evcdec.c | 276 +++++++++++++++++++++++++++++++ 6 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 libavcodec/evc_frame_merge_bsf.c create mode 100644 libavformat/evcdec.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index a28919992a..0ce8fe5b9c 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1258,6 +1258,7 @@ OBJS-$(CONFIG_VP9_METADATA_BSF) += vp9_metadata_bsf.o OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += vp9_superframe_split_bsf.o +OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += evc_frame_merge_bsf.o # thread libraries OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index e8216819ca..7512fccc78 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -65,16 +65,17 @@ extern const FFBitStreamFilter ff_vp9_metadata_bsf; extern const FFBitStreamFilter ff_vp9_raw_reorder_bsf; extern const FFBitStreamFilter ff_vp9_superframe_bsf; extern const FFBitStreamFilter ff_vp9_superframe_split_bsf; +extern const FFBitStreamFilter ff_evc_frame_merge_bsf; #include "libavcodec/bsf_list.c" const AVBitStreamFilter *av_bsf_iterate(void **opaque) { - uintptr_t i = (uintptr_t)*opaque; + uintptr_t i = (uintptr_t) * opaque; const FFBitStreamFilter *f = bitstream_filters[i]; if (f) { - *opaque = (void*)(i + 1); + *opaque = (void *)(i + 1); return &f->p; } return NULL; diff --git a/libavcodec/evc_frame_merge_bsf.c b/libavcodec/evc_frame_merge_bsf.c new file mode 100644 index 0000000000..f2082376ac --- /dev/null +++ b/libavcodec/evc_frame_merge_bsf.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019 James Almer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "get_bits.h" +#include "golomb.h" +#include "bsf.h" +#include "bsf_internal.h" +#include "avcodec.h" + +#include "evc.h" +#include "evc_parse.h" + +#define INIT_AU_BUF_CAPACITY 1024 + +// Access unit data +typedef struct AccessUnitBuffer { + uint8_t *data; // the data buffer + size_t data_size; // size of data in bytes + size_t capacity; // buffer capacity +} AccessUnitBuffer; + +typedef struct EVCFMergeContext { + AVPacket *in; + EVCParserContext parser_ctx; + AccessUnitBuffer au_buffer; +} EVCFMergeContext; + +static int end_of_access_unit_found(EVCParserContext *parser_ctx) +{ + if (parser_ctx->profile == 0) { // BASELINE profile + if (parser_ctx->nalu_type == EVC_NOIDR_NUT || parser_ctx->nalu_type == EVC_IDR_NUT) + return 1; + } else { // MAIN profile + if (parser_ctx->nalu_type == EVC_NOIDR_NUT) { + if (parser_ctx->poc.PicOrderCntVal != parser_ctx->poc.prevPicOrderCntVal) + return 1; + } else if (parser_ctx->nalu_type == EVC_IDR_NUT) + return 1; + } + return 0; +} + +static void evc_frame_merge_flush(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + av_packet_unref(ctx->in); +} + +static int evc_frame_merge_filter(AVBSFContext *bsf, AVPacket *out) +{ + EVCFMergeContext *ctx = bsf->priv_data; + EVCParserContext *parser_ctx = &ctx->parser_ctx; + + AVPacket *in = ctx->in; + + int free_space = 0; + size_t nalu_size = 0; + uint8_t *nalu = NULL; + int au_end_found = 0; + int err; + + err = ff_bsf_get_packet_ref(bsf, in); + if (err < 0) + return err; + + nalu_size = av_evc_read_nal_unit_length(in->data, EVC_NALU_LENGTH_PREFIX_SIZE, bsf); + if (nalu_size <= 0) { + av_packet_unref(in); + return AVERROR_INVALIDDATA; + } + + nalu = in->data + EVC_NALU_LENGTH_PREFIX_SIZE; + nalu_size = in->size - EVC_NALU_LENGTH_PREFIX_SIZE; + + // NAL unit parsing needed to determine if end of AU was found + err = ff_evc_parse_nal_unit(parser_ctx, nalu, nalu_size, bsf); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "NAL Unit parsing error\n"); + av_packet_unref(in); + + return err; + } + + au_end_found = end_of_access_unit_found(parser_ctx); + + free_space = ctx->au_buffer.capacity - ctx->au_buffer.data_size; + while (free_space < in->size) { + ctx->au_buffer.capacity *= 2; + free_space = ctx->au_buffer.capacity - ctx->au_buffer.data_size; + + if (free_space >= in->size) + ctx->au_buffer.data = av_realloc(ctx->au_buffer.data, ctx->au_buffer.capacity); + } + + memcpy(ctx->au_buffer.data + ctx->au_buffer.data_size, in->data, in->size); + + ctx->au_buffer.data_size += in->size; + + av_packet_unref(in); + + if (au_end_found) { + uint8_t *data = av_memdup(ctx->au_buffer.data, ctx->au_buffer.data_size); + err = av_packet_from_data(out, data, ctx->au_buffer.data_size); + + ctx->au_buffer.data_size = 0; + } else + err = AVERROR(EAGAIN); + + if (err < 0 && err != AVERROR(EAGAIN)) + evc_frame_merge_flush(bsf); + + return err; +} + +static int evc_frame_merge_init(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + ctx->in = av_packet_alloc(); + if (!ctx->in) + return AVERROR(ENOMEM); + + ctx->au_buffer.capacity = INIT_AU_BUF_CAPACITY; + ctx->au_buffer.data = av_malloc(INIT_AU_BUF_CAPACITY); + ctx->au_buffer.data_size = 0; + + return 0; +} + +static void evc_frame_merge_close(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + av_packet_free(&ctx->in); + + ctx->au_buffer.capacity = 0; + av_freep(&ctx->au_buffer.data); + ctx->au_buffer.data_size = 0; +} + +static const enum AVCodecID evc_frame_merge_codec_ids[] = { + AV_CODEC_ID_EVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_evc_frame_merge_bsf = { + .p.name = "evc_frame_merge", + .p.codec_ids = evc_frame_merge_codec_ids, + .priv_data_size = sizeof(EVCFMergeContext), + .init = evc_frame_merge_init, + .flush = evc_frame_merge_flush, + .close = evc_frame_merge_close, + .filter = evc_frame_merge_filter, +}; diff --git a/libavformat/Makefile b/libavformat/Makefile index f31135d806..6e4231fda2 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -251,6 +251,7 @@ OBJS-$(CONFIG_HCOM_DEMUXER) += hcom.o pcm.o OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o +OBJS-$(CONFIG_EVC_DEMUXER) += evcdec.o rawdec.o OBJS-$(CONFIG_EVC_MUXER) += rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index d3871de268..ae604236ae 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -154,6 +154,7 @@ extern const AVInputFormat ff_ea_cdata_demuxer; extern const AVInputFormat ff_eac3_demuxer; extern const FFOutputFormat ff_eac3_muxer; extern const AVInputFormat ff_epaf_demuxer; +extern const AVInputFormat ff_evc_demuxer; extern const FFOutputFormat ff_evc_muxer; extern const FFOutputFormat ff_f4v_muxer; extern const AVInputFormat ff_ffmetadata_demuxer; diff --git a/libavformat/evcdec.c b/libavformat/evcdec.c new file mode 100644 index 0000000000..89eda0f53e --- /dev/null +++ b/libavformat/evcdec.c @@ -0,0 +1,276 @@ +/* + * RAW EVC video demuxer + * + * Copyright (c) 2021 Dawid Kozinski + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavcodec/get_bits.h" +#include "libavcodec/golomb.h" +#include "libavcodec/internal.h" +#include "libavcodec/evc.h" +#include "libavcodec/bsf.h" + +#include "libavutil/opt.h" + +#include "rawdec.h" +#include "avformat.h" +#include "internal.h" + + +#define RAW_PACKET_SIZE 1024 + +typedef struct EVCParserContext { + int got_sps; + int got_pps; + int got_idr; + int got_nonidr; + +} EVCParserContext; + +typedef struct EVCDemuxContext { + const AVClass *class; + AVRational framerate; + + AVBSFContext *bsf; + +} EVCDemuxContext; + +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define OFFSET(x) offsetof(EVCDemuxContext, x) +static const AVOption evc_options[] = { + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC}, + { NULL }, +}; +#undef OFFSET + +static const AVClass evc_demuxer_class = { + .class_name = "EVC Annex B demuxer", + .item_name = av_default_item_name, + .option = evc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int get_nalu_type(const uint8_t *bits, int bits_size) +{ + int unit_type_plus1 = 0; + + if (bits_size >= EVC_NALU_HEADER_SIZE) { + unsigned char *p = (unsigned char *)bits; + // forbidden_zero_bit + if ((p[0] & 0x80) != 0) // Cannot get bitstream information. Malformed bitstream. + return -1; + + // nal_unit_type + unit_type_plus1 = (p[0] >> 1) & 0x3F; + } + + return unit_type_plus1 - 1; +} + +static uint32_t read_nal_unit_length(const uint8_t *bits, int bits_size) +{ + uint32_t nalu_len = 0; + + if (bits_size >= EVC_NALU_LENGTH_PREFIX_SIZE) { + + int t = 0; + unsigned char *p = (unsigned char *)bits; + + for (int i = 0; i < EVC_NALU_LENGTH_PREFIX_SIZE; i++) + t = (t << 8) | p[i]; + + nalu_len = t; + if (nalu_len == 0) // Invalid bitstream size + return 0; + } + + return nalu_len; +} + +static int parse_nal_units(const AVProbeData *p, EVCParserContext *ev) +{ + int nalu_type; + size_t nalu_size; + unsigned char *bits = (unsigned char *)p->buf; + int bytes_to_read = p->buf_size; + + while (bytes_to_read > EVC_NALU_LENGTH_PREFIX_SIZE) { + + nalu_size = read_nal_unit_length(bits, EVC_NALU_LENGTH_PREFIX_SIZE); + if (nalu_size == 0) break; + + bits += EVC_NALU_LENGTH_PREFIX_SIZE; + bytes_to_read -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if(bytes_to_read < nalu_size) break; + + nalu_type = get_nalu_type(bits, bytes_to_read); + + if (nalu_type == EVC_SPS_NUT) + ev->got_sps++; + else if (nalu_type == EVC_PPS_NUT) + ev->got_pps++; + else if (nalu_type == EVC_IDR_NUT ) + ev->got_idr++; + else if (nalu_type == EVC_NOIDR_NUT) + ev->got_nonidr++; + + bits += nalu_size; + bytes_to_read -= nalu_size; + } + + return 0; +} + +static int annexb_probe(const AVProbeData *p) +{ + EVCParserContext ev = {0}; + int ret = parse_nal_units(p, &ev); + + if (ret == 0 && ev.got_sps && ev.got_pps && (ev.got_idr || ev.got_nonidr > 3)) + return AVPROBE_SCORE_EXTENSION + 1; // 1 more than .mpg + + return 0; +} + +static int evc_read_header(AVFormatContext *s) +{ + AVStream *st; + FFStream *sti; + const AVBitStreamFilter *filter = av_bsf_get_by_name("evc_frame_merge"); + EVCDemuxContext *c = s->priv_data; + int ret = 0; + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } + sti = ffstream(st); + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_EVC; + + // This causes sending to the parser full frames, not chunks of data + // The flag PARSER_FLAG_COMPLETE_FRAMES will be set in demux.c (demux.c: 1316) + sti->need_parsing = AVSTREAM_PARSE_HEADERS; + + st->avg_frame_rate = c->framerate; + st->codecpar->framerate = c->framerate; + + // taken from rawvideo demuxers + avpriv_set_pts_info(st, 64, 1, 1200000); + + ret = av_bsf_alloc(filter, &c->bsf); + if (ret < 0) + return ret; + + ret = avcodec_parameters_copy(c->bsf->par_in, st->codecpar); + if (ret < 0) + return ret; + + ret = av_bsf_init(c->bsf); + if (ret < 0) + return ret; + +fail: + return ret; +} + +static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + int32_t nalu_size; + int au_end_found; + + EVCDemuxContext *const c = s->priv_data; + + int eof = avio_feof (s->pb); + if(eof) { + av_packet_unref(pkt); + return AVERROR_EOF; + } + + au_end_found = 0; + + while(!au_end_found) { + + uint8_t buf[EVC_NALU_LENGTH_PREFIX_SIZE]; + ret = avio_read(s->pb, (unsigned char *)&buf, EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) { + av_packet_unref(pkt); + return ret; + } + + nalu_size = read_nal_unit_length((const uint8_t *)&buf, EVC_NALU_LENGTH_PREFIX_SIZE); + if(nalu_size <= 0) { + av_packet_unref(pkt); + return -1; + } + + avio_seek(s->pb, -EVC_NALU_LENGTH_PREFIX_SIZE, SEEK_CUR); + + ret = av_get_packet(s->pb, pkt, nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + if (ret != (nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE)) + return AVERROR(EIO); + + ret = av_bsf_send_packet(c->bsf, pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to send packet to " + "evc_frame_merge filter\n"); + return ret; + } + + ret = av_bsf_receive_packet(c->bsf, pkt); + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) + av_log(s, AV_LOG_ERROR, "evc_frame_merge filter failed to " + "send output packet\n"); + + au_end_found = 1; + if (ret == AVERROR(EAGAIN)) + au_end_found = 0; + } + + return ret; +} + +static int evc_read_close(AVFormatContext *s) +{ + EVCDemuxContext *const c = s->priv_data; + + av_bsf_free(&c->bsf); + return 0; +} + +const AVInputFormat ff_evc_demuxer = { + .name = "evc", + .long_name = NULL_IF_CONFIG_SMALL("EVC Annex B"), + .read_probe = annexb_probe, + .read_header = evc_read_header, // annexb_read_header + .read_packet = evc_read_packet, // annexb_read_packet + .read_close = evc_read_close, + .extensions = "evc", + .flags = AVFMT_GENERIC_INDEX, + .flags_internal = FF_FMT_INIT_CLEANUP, + .raw_codec_id = AV_CODEC_ID_EVC, + .priv_data_size = sizeof(EVCDemuxContext), + .priv_class = &evc_demuxer_class, +};