From patchwork Fri Aug 4 17:20:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Tomas_H=C3=A4rdin?= X-Patchwork-Id: 4624 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.46.211 with SMTP id u202csp2540510vsu; Fri, 4 Aug 2017 10:21:03 -0700 (PDT) X-Received: by 10.28.58.138 with SMTP id h132mr1680624wma.64.1501867263742; Fri, 04 Aug 2017 10:21:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1501867263; cv=none; d=google.com; s=arc-20160816; b=VD/N3WAEYc1Wljp7yMyd19VPwd33GXfkrVQUf4xCbQLe1zbICGX2g/W/bFxAOQf7Ap /l2RJ2Z9UK0noNxBy6unHSU0p/lWBxT9wsGT+LqvvzKaadDm5F0MnvxRucq49ZLgqD+G dgHGxRnVMKNsECsjHajCR21JtV/HDC6m+3Rk5LFRG8mIxB7yeAnzXkkiPF0VrX5oSdyZ InLBREP8beUOco25lMLX3YZHV4CWcn8rasB8MkSSe44q0CWnMYfLp+3mDy9m95R7nRn7 5XTfSNQVmgdOsmgeswllYEj8kBZW1bN4DsK8VTE0HzLV5TN0AeWLQDwX7Kbg654iEQmO sZ+w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:references:in-reply-to:date:to:from:message-id :dkim-signature:dkim-signature:delivered-to :arc-authentication-results; bh=WTqav3xGHRaEXwpukjgIC+duRm1qgy5BQfR+C8GGvAA=; b=KW2NOJZWWFBq0GQRouEqpOvTYtSUZ2K/fRRwKXZnkf9AgH5yp1EHshB9pQgzY0DxzA /AVGkqxsSGmA0q06iqJBz89l4WyTRH5hJ1i+ZKmKoPG7v1TUn1ZN0u6YS+jfgIWxclhp EC1jeIekt78q6vYff2uzLKhvyuonY0vLw4nIYwOBRMS2mB5QghSScjCNJljOJ3w2O/RC cITm1/Fm7YVtbxhSbDAob6kURe9WIwUIjE1IvNC17w3g4X3lgK0VxZdK+EZwEwVqRZGm Tqu5AmcK+wXuxvMBCbBUC+/dCh/Nk0XVhDWBylEG987mAlNEvweBvVyCOEsthuh58ldW LWEg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@acc.umu.se header.b=XGta2on/; dkim=neutral (body hash did not verify) header.i=@acc.umu.se header.b=XGta2on/; 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 Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id x46si3913205wrb.449.2017.08.04.10.21.02; Fri, 04 Aug 2017 10:21:03 -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=@acc.umu.se header.b=XGta2on/; dkim=neutral (body hash did not verify) header.i=@acc.umu.se header.b=XGta2on/; 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 Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 14E63689A4C; Fri, 4 Aug 2017 20:20:59 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail.acc.umu.se (mail.acc.umu.se [130.239.18.156]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1E075689760 for ; Fri, 4 Aug 2017 20:20:53 +0300 (EEST) Received: from localhost (localhost.localdomain [127.0.0.1]) by amavisd-new (Postfix) with ESMTP id 54E3944B94 for ; Fri, 4 Aug 2017 19:20:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=acc.umu.se; s=mail1; t=1501867252; bh=GUJw1bEl2bLWPmZdC+Oy1HiWhIsmi8Sgh4SxEXeT6xE=; h=Subject:From:To:Date:In-Reply-To:References:From; b=XGta2on/pVQw/V0bw4WQD2ecZrhl5v6JQzYIXKnDxQOOWyTvTz7Xalq4f6FHgsPcA nRdv0FcUlpRCDLSwvMW6OQIc1OjoXlBEF4f3n3QPTVGA3xTHy3DdXm46VD77d28Xwc xhaS/tIknrd0hI3MvTeaTJKfV8D3QXI13QvYa0XbekIxMkWlQcsyW/camoSUVyoA9z L6POWwVUwRFYU+bbimRDfNpf9z8WoSq97+4Fe6siEYvyabKTwhbDLCxnRP4q5YbTpO BiWNiwBW8ppjMGFjNfYZaSAowaceVYShph1UxZpH8XHGYABd4YtPUD+aLZORgCiYAF JpFCj2I5hymtg== Received: from aspire (h-39-109.A258.priv.bahnhof.se [79.136.39.109]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) (Authenticated sender: tjoppen) by mail.acc.umu.se (Postfix) with ESMTPSA id 11C1044B90 for ; Fri, 4 Aug 2017 19:20:51 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=acc.umu.se; s=mail1; t=1501867252; bh=GUJw1bEl2bLWPmZdC+Oy1HiWhIsmi8Sgh4SxEXeT6xE=; h=Subject:From:To:Date:In-Reply-To:References:From; b=XGta2on/pVQw/V0bw4WQD2ecZrhl5v6JQzYIXKnDxQOOWyTvTz7Xalq4f6FHgsPcA nRdv0FcUlpRCDLSwvMW6OQIc1OjoXlBEF4f3n3QPTVGA3xTHy3DdXm46VD77d28Xwc xhaS/tIknrd0hI3MvTeaTJKfV8D3QXI13QvYa0XbekIxMkWlQcsyW/camoSUVyoA9z L6POWwVUwRFYU+bbimRDfNpf9z8WoSq97+4Fe6siEYvyabKTwhbDLCxnRP4q5YbTpO BiWNiwBW8ppjMGFjNfYZaSAowaceVYShph1UxZpH8XHGYABd4YtPUD+aLZORgCiYAF JpFCj2I5hymtg== Message-ID: <1501867247.24518.3.camel@acc.umu.se> From: Tomas =?ISO-8859-1?Q?H=E4rdin?= To: FFmpeg development discussions and patches Date: Fri, 04 Aug 2017 19:20:47 +0200 In-Reply-To: <20170803202454.ok3d7dkvarqonc2d@reimardoeffinger.de> References: <1501713604.19937.27.camel@acc.umu.se> <20170803202454.ok3d7dkvarqonc2d@reimardoeffinger.de> X-Mailer: Evolution 3.18.5.1-1 Mime-Version: 1.0 Subject: Re: [FFmpeg-devel] [WIP] libcodec2 wrapper + de/muxer in FFmpeg X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" On Thu, 2017-08-03 at 22:24 +0200, Reimar Döffinger wrote: > On Thu, Aug 03, 2017 at 12:40:04AM +0200, Tomas Härdin wrote: > > > > > +    //statically assert the size of avpriv_codec2_header > > +    //putting it here because all codec2 things depend on > > codec2utils > > +    switch(0) { > > +    case 0: > > +    case sizeof(avpriv_codec2_header) == 7: //if false then the > > compiler will complain > > +        break; > > +    } > I was pretty sure we had some kind of static assert already, > based on negative array size in a struct (so it can work outside > functions)... > But doesn't matter due to next comments... > > > > > +        *((avpriv_codec2_header*)avctx->extradata) = > > avpriv_codec2_make_header(mode); > I am pretty sure this is a strict aliasing violation. > Even ignoring the ugliness and performance issues with > returning structs and assigning them causing lots of > pointless copies. > Also, since all struct elements are bytes, it isn't > really any more readable than just assigning to the > byte array, with the names you not have in the struct > in comments instead. > And that is without going into all the ways someone could > change that struct (e.g. in case of new features) that > would completely break this kind of code due to endianness, > alignment, ... > Even just supporting both the current and a potential future version > that changes any of the fields would be hard with this kind of code. Aliasing violation is a good point, but it also doesn't matter here. extradata isn't used anywhere else in the function. The struct is mostly a handy way of getting names for all the bytes, so a enum with offsets could work just as well: enum avpriv_codec2_header_bytes {   AVPRIV_CODEC2_MAGIC0 = 0,   AVPRIV_CODEC2_MAGIC1,   AVPRIV_CODEC2_MAGIC2,   AVPRIV_CODEC2_VERSION_MAJOR,   ... }; Endianness is also not an issue since it's all bytes. But: one nice way to do this is perhaps a pair of functions for parsing / creating the struct out of the byte array and vice versa. > > > > +        if (avctx->extradata_size != sizeof(avpriv_codec2_header)) > > { > > +            av_log(avctx, AV_LOG_ERROR, "must have exactly %zu > > bytes of extradata (got %i)\n", > > +                   sizeof(avpriv_codec2_header), avctx- > > >extradata_size); > > +        } > I would think at least if it is less you wouldn't want to just > continue? Even if extradata is required to be padded (is it?), > blindly selecting 0 as mode doesn't seem very likely to be right. This has been fixed in the updated patchset > > > > +    output = (int16_t *)frame->data[0]; > The codec2 documentation seems pretty non-existant, so I > assume you are right that this is always in native endianness. > However from what I can see codec2 actually uses the "short" type, > not int16_t. > While it shouldn't make a difference in practice, if casting anyway > I'd suggest using the type matching the API. Some kind of static assert for sizeof(short) == sizeof(int16_t) would be nicest I think. If someone's on a platform with non-16-bit shorts and they want --enable-libcodec2 to work then they can probably write and submit a patch. > > > > +    if (nframes > 0) { > > +        *got_frame_ptr = 1; > > +    } > Not just *got_frame_ptr = nframes > 0; ? Sure. > > > > +    int16_t *samples = (int16_t *)frame->data[0]; > You are casting the const away. > Did they just forget to add the const to the API? > If so, can you suggest it to be added? > Otherwise if it's intentional you need to make a copy. No const in the API, but adding it is of course easy enough. That won't get into Debian for some time however, so users would have to build their own libcodec2. Version #defines in could help with this, currently there are none so that's one way to detect the non-const API. I'll have to run that past the other guys. > > +    int ret; > > + > > +    if ((ret = ff_alloc_packet2(avctx, avpkt, avctx->block_align, > > 0)) < 0) { > If you merge the declaration and assignment it you would get > for free not having the assignment inside the if, which I > still think is just horrible style. :) Fair enough. I use this style in a few places when stacking a bunch of inits and checks for ENOMEM and such. doc/developer.html doesn't seem to have a preference, and I've seen the style used elsewhere in FFmpeg. I went with splitting the assignments and testing in all the ifs in the updated patchset > > > > +    //file starts wih 0xC0DEC2 > > +    if (p->buf[0] == 0xC0 && p->buf[1] == 0xDE && p->buf[2] == > > 0xC2) { > > +        return AVPROBE_SCORE_MAX; > > +    } > As mentioned, try to find a few more bits and reduce the score. > If only these 3 bytes match, I would suggest a rather low score. > (I doubt there are enough useful bits in this header to allow > reaching MAX score, it's a shame they didn't invest a byte or > 2 more, especially considering that while 0xC0DEC2 might look > clever it doesn't exactly get maximum entropy out of those 3 bytes). Maximum entropy by what measure? :) The probing logic has been expanded, see the updated patchset I sent as reply to Nicolas. A file extension check might also be in order, and being one or two points away from MAX in all cases. I'm also curious what format it could conceivably mis-probed as, beyond purposeful polyglots like MXF. .au? We also toyed with the idea of a very long format string on IRC yesterday since things are still very much experimental and may change rapidly > > > > +    AVStream *st; > > + > > +    if (!(st = avformat_new_stream(s, NULL)) || > Can merge allocation and declaration again. Fixed > > > > +    //Read roughly 0.1 seconds worth of data. > > +    n = st->codecpar->bit_rate / ((int)(8/0.1) * block_align) + 1; > That doesn't seem overly readable to me... > // Read about 1/10th of a second worth of data > st->codecpar->bit_rate / 10 / 8 / block_align + 1 > > Seems not really worse and doesn't have doubles and casts... > Otherwise > blocks_per_second = st->codecpar->bit_rate / 8 / block_align; > n = blocks_per_second / 10; > might be even clearer. Actually, I might go with an option and default to just demuxing one frame at a time. So maximum seek fidelity and minimum delay unless the user wants to transcode large amounts of speech > > > > +    if (av_new_packet(pkt, size) < 0) > > +        return AVERROR(ENOMEM); > > +    //try to read desired number of bytes, recompute n from to > > actual number of bytes read > > +    pkt->pos= avio_tell(s->pb); > > +    pkt->stream_index = 0; > > +    ret = ffio_read_partial(s->pb, pkt->data, size); > > +    if (ret < 0) { > > +        av_packet_unref(pkt); > > +        return ret; > > +    } > > +    av_shrink_packet(pkt, ret); > Ouch, why are you not just using av_get_packet? Well, I nicked it from ff_raw_read_partial_packet(). av_get_packet() does not seem to respect block_align, which is worrying. Went with it either way for now. > > > > +    avio_write(s->pb, st->codecpar->extradata, > > sizeof(avpriv_codec2_header)); > Error check? avio_write() returns void :) > > > > +    if (!(st = avformat_new_stream(s, NULL)) || > > +        ff_alloc_extradata(st->codecpar, > > sizeof(avpriv_codec2_header))) { > > +        return AVERROR(ENOMEM); > Here and in the other read_header, this would preferably preserve the > error that > ff_alloc_extradata returned. Fixed. --- When setting up a minimal build for afl-fuzz:   ./configure --cc=/usr/bin/afl-clang-fast \     --extra-cflags='-DAFL_HARDEN=1' --disable-everything \     --enable-demuxer=codec2 --enable-libcodec2 \     --enable-decoder=libcodec2 --enable-protocol=file \     --disable-demuxer=rtsp --disable-avfilter I got AR complaining about not finding libavformat/codec2utils.o. So I had to change the OBJS to explicitly point out lavc like so: OBJS-$(CONFIG_CODEC2_DEMUXER)            += ../libavcodec/codec2utils.o codec2.o rawdec.o pcm.o OBJS-$(CONFIG_CODEC2_MUXER)              += ../libavcodec/codec2utils.o codec2.o rawenc.o OBJS-$(CONFIG_CODEC2RAW_DEMUXER)         += ../libavcodec/codec2utils.o codec2.o rawdec.o pcm.o Am I missing something there? Surely more demuxer make use of lavc.. dv.c has this only, despite #including "libavcodec/dv.h": OBJS-$(CONFIG_DV_DEMUXER)                += dv.o Despite depending on lavc-- ohhh! I may need a codec2utils target after all, for it to be built into lavc which lavf already depends on :) Or? TODO: * have -mode be an integer and use the CONST system Nicolas mentioned * option for demuxing multiple frames at a time * sort the extradata aliasing thing * address API issues in libcodec2, possibly modify the format (there should still be time) New patchset attached. Has the new probe logic and all that in it. 85 cycles of afl-fuzz without hangs or crashes. /Tomas From 2aef2d364fd93059ac9092f4675192749a5e91c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= Date: Wed, 2 Aug 2017 22:17:19 +0200 Subject: [PATCH 1/2] Add codec2 muxers, demuxers and en/decoder via libcodec2 --- Changelog | 2 + configure | 5 + doc/general.texi | 13 +++ libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec2utils.c | 110 ++++++++++++++++++ libavcodec/codec2utils.h | 86 ++++++++++++++ libavcodec/codec_desc.c | 7 ++ libavcodec/libcodec2.c | 199 +++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- libavformat/Makefile | 4 + libavformat/allformats.c | 2 + libavformat/codec2.c | 285 +++++++++++++++++++++++++++++++++++++++++++++++ libavformat/rawenc.c | 13 +++ libavformat/utils.c | 1 + libavformat/version.h | 2 +- 17 files changed, 733 insertions(+), 2 deletions(-) create mode 100644 libavcodec/codec2utils.c create mode 100644 libavcodec/codec2utils.h create mode 100644 libavcodec/libcodec2.c create mode 100644 libavformat/codec2.c diff --git a/Changelog b/Changelog index 187ae7950a..e28da7dcc4 100644 --- a/Changelog +++ b/Changelog @@ -29,6 +29,8 @@ version : - limiter video filter - libvmaf video filter - Dolby E decoder and SMPTE 337M demuxer +- codec2 en/decoding via libcodec2 +- muxer/demuxer for raw codec2 files and .c2 files version 3.3: - CrystalHD decoder moved to new decode API diff --git a/configure b/configure index 66c7b948e4..50f455af71 100755 --- a/configure +++ b/configure @@ -220,6 +220,7 @@ External library support: --enable-libcaca enable textual display using libcaca [no] --enable-libcelt enable CELT decoding via libcelt [no] --enable-libcdio enable audio CD grabbing with libcdio [no] + --enable-libcodec2 enable codec2 en/decoding using libcodec2 [no] --enable-libdc1394 enable IIDC-1394 grabbing using libdc1394 and libraw1394 [no] --enable-libfdk-aac enable AAC de/encoding via libfdk-aac [no] @@ -1540,6 +1541,7 @@ EXTERNAL_LIBRARY_LIST=" libbs2b libcaca libcelt + libcodec2 libdc1394 libflite libfontconfig @@ -2863,6 +2865,8 @@ pcm_mulaw_at_encoder_select="audio_frame_queue" chromaprint_muxer_deps="chromaprint" h264_videotoolbox_encoder_deps="videotoolbox_encoder pthreads" libcelt_decoder_deps="libcelt" +libcodec2_decoder_deps="libcodec2" +libcodec2_encoder_deps="libcodec2" libfdk_aac_decoder_deps="libfdk_aac" libfdk_aac_encoder_deps="libfdk_aac" libfdk_aac_encoder_select="audio_frame_queue" @@ -5837,6 +5841,7 @@ enabled libcelt && require libcelt celt/celt.h celt_decode -lcelt0 && { check_lib libcelt celt/celt.h celt_decoder_create_custom -lcelt0 || die "ERROR: libcelt must be installed and version must be >= 0.11.0."; } enabled libcaca && require_pkg_config caca caca.h caca_create_canvas +enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lcodec2 enabled libdc1394 && require_pkg_config libdc1394-2 dc1394/dc1394.h dc1394_new enabled libfdk_aac && { use_pkg_config fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen || { require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac && diff --git a/doc/general.texi b/doc/general.texi index 036c8c25d4..4bcc2b2d91 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -85,6 +85,15 @@ Go to @url{http://www.twolame.org/} and follow the instructions for installing the library. Then pass @code{--enable-libtwolame} to configure to enable it. +@section libcodec2 + +FFmpeg can make use of libcodec2 to codec2 encoding and decoding. +There is currently no native decoder, so libcodec2 must be used for decoding. + +Go to @url{http://freedv.org/}, download "Codec 2 source archive". +Build and install using CMake. Debian users can install the libcodec2-dev package instead. +Once libcodec2 is installed you can pass @code{--enable-libcodec2} to configure to enable it. + @section libvpx FFmpeg can make use of the libvpx library for VP8/VP9 encoding. @@ -290,6 +299,8 @@ library: @item BRSTM @tab @tab X @tab Audio format used on the Nintendo Wii. @item BWF @tab X @tab X +@item codec2 (raw) @tab X @tab X +@item codec2 (.c2 files) @tab X @tab X @item CRI ADX @tab X @tab X @tab Audio-only format used in console video games. @item Discworld II BMV @tab @tab X @@ -994,6 +1005,8 @@ following image formats are supported: @tab Used in Bink and Smacker files in many games. @item CELT @tab @tab E @tab decoding supported through external library libcelt +@item codec2 @tab E @tab E + @tab en/decoding supported through external library libcodec2 @item Delphine Software International CIN audio @tab @tab X @tab Codec used in Delphine Software International games. @item Digital Speech Standard - Standard Play mode (DSS SP) @tab @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 74de41ab0f..dab3d00f30 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -885,6 +885,8 @@ OBJS-$(CONFIG_ILBC_AT_ENCODER) += audiotoolboxenc.o OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER) += audiotoolboxenc.o OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER) += audiotoolboxenc.o OBJS-$(CONFIG_LIBCELT_DECODER) += libcelt_dec.o +OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o codec2utils.o +OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o codec2utils.o OBJS-$(CONFIG_LIBFDK_AAC_DECODER) += libfdk-aacdec.o OBJS-$(CONFIG_LIBFDK_AAC_ENCODER) += libfdk-aacenc.o OBJS-$(CONFIG_LIBGSM_DECODER) += libgsmdec.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 4712592a5f..5296fac507 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -618,6 +618,7 @@ static void register_all(void) REGISTER_DECODER(QDMC_AT, qdmc_at); REGISTER_DECODER(QDM2_AT, qdm2_at); REGISTER_DECODER(LIBCELT, libcelt); + REGISTER_ENCDEC (LIBCODEC2, libcodec2); REGISTER_ENCDEC (LIBFDK_AAC, libfdk_aac); REGISTER_ENCDEC (LIBGSM, libgsm); REGISTER_ENCDEC (LIBGSM_MS, libgsm_ms); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index c594993766..488eb8b1f5 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -622,6 +622,7 @@ enum AVCodecID { AV_CODEC_ID_PAF_AUDIO, AV_CODEC_ID_ON2AVC, AV_CODEC_ID_DSS_SP, + AV_CODEC_ID_CODEC2, AV_CODEC_ID_FFWAVESYNTH = 0x15800, AV_CODEC_ID_SONIC, diff --git a/libavcodec/codec2utils.c b/libavcodec/codec2utils.c new file mode 100644 index 0000000000..d6d095dea0 --- /dev/null +++ b/libavcodec/codec2utils.c @@ -0,0 +1,110 @@ +/* + * codec2 utility functions + * Copyright (c) 2017 Tomas Härdin + * + * 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 +#include "internal.h" +#include "libavcodec/codec2utils.h" + +int avpriv_codec2_mode_from_str(void *logctx, const char *modestr) +{ + //statically assert the size of avpriv_codec2_header + //putting it here because all codec2 things depend on codec2utils + switch(0) { + case 0: + case sizeof(avpriv_codec2_header) == 7: //if false then the compiler will complain + break; + } + + if (!modestr) { + av_log(logctx, AV_LOG_ERROR, "raw codec2 streams need -mode set\n"); + return AVERROR(EINVAL); + } + +#define MATCH(x) do { if (!strcmp(modestr, #x)) { return CODEC2_MODE_##x; } } while (0) + MATCH(3200); + MATCH(2400); + MATCH(1600); + MATCH(1400); + MATCH(1300); + MATCH(1200); + MATCH(700); + MATCH(700B); + MATCH(700C); + + av_log(logctx, AV_LOG_ERROR, "invalid codec2 mode: %s\n", modestr); + return AVERROR(EINVAL); +} + +int avpriv_codec2_mode_bit_rate(void *logctx, int mode) +{ + int frame_size = avpriv_codec2_mode_frame_size(logctx, mode); + int block_align = avpriv_codec2_mode_block_align(logctx, mode); + + if (frame_size <= 0 || block_align <= 0) { + return 0; + } + + return 8 * 8000 * block_align / frame_size; +} + +int avpriv_codec2_mode_frame_size(void *logctx, int mode) +{ + int frame_size_table[AVPRIV_CODEC2_MAX_KNOWN_MODE+1] = { + 160, // 3200 + 160, // 2400 + 320, // 1600 + 320, // 1400 + 320, // 1300 + 320, // 1200 + 320, // 700 + 320, // 700B + 320, // 700C + }; + + if (mode < 0 || mode > AVPRIV_CODEC2_MAX_KNOWN_MODE) { + av_log(logctx, AV_LOG_ERROR, "unknown codec2 mode %i, can't find frame_size\n", mode); + return 0; + } else { + return frame_size_table[mode]; + } +} + +int avpriv_codec2_mode_block_align(void *logctx, int mode) +{ + int block_align_table[AVPRIV_CODEC2_MAX_KNOWN_MODE+1] = { + 8, // 3200 + 6, // 2400 + 8, // 1600 + 7, // 1400 + 7, // 1300 + 6, // 1200 + 4, // 700 + 4, // 700B + 4, // 700C + }; + + if (mode < 0 || mode > AVPRIV_CODEC2_MAX_KNOWN_MODE) { + av_log(logctx, AV_LOG_ERROR, "unknown codec2 mode %i, can't find block_align\n", mode); + return 0; + } else { + return block_align_table[mode]; + } +} diff --git a/libavcodec/codec2utils.h b/libavcodec/codec2utils.h new file mode 100644 index 0000000000..c28a253ac6 --- /dev/null +++ b/libavcodec/codec2utils.h @@ -0,0 +1,86 @@ +/* + * codec2 utility functions + * Copyright (c) 2017 Tomas Härdin + * + * Constants taken from codec2.h in libcodec2, which is also under the LGPL 2.1 + * Copyright (C) 2010 David Rowe + * + * 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 + */ + +#ifndef AVCODEC_CODEC2UTILS_H +#define AVCODEC_CODEC2UTILS_H + +#include + +#ifndef __CODEC2__ +//from codec2.h, repeated here to avoid a dependency +#define CODEC2_MODE_3200 0 +#define CODEC2_MODE_2400 1 +#define CODEC2_MODE_1600 2 +#define CODEC2_MODE_1400 3 +#define CODEC2_MODE_1300 4 +#define CODEC2_MODE_1200 5 +#define CODEC2_MODE_700 6 +#define CODEC2_MODE_700B 7 +#define CODEC2_MODE_700C 8 +#endif + +#define AVPRIV_CODEC2_MAX_KNOWN_MODE CODEC2_MODE_700C + +//Converts strings like "1400" -> 3 and so on +//logctx is used for av_log() +//Returns <0 if modestr is invalid +int avpriv_codec2_mode_from_str(void *logctx, const char *modestr); + +//The three following functions are here to avoid needing libavformat/codec2.c to depend on libcodec2 + +//Computes bitrate from mode, with frames rounded up to the nearest octet. +//So 700 bit/s (28 bits/frame) becomes 800 bits/s (32 bits/frame). +//logctx is used for av_log() +//Returns <0 if mode is invalid +int avpriv_codec2_mode_bit_rate(void *logctx, int mode); + +//duplicates codec2_samples_per_frame() +int avpriv_codec2_mode_frame_size(void *logctx, int mode); + +//duplicates (codec2_bits_per_frame()+7)/8 +int avpriv_codec2_mode_block_align(void *logctx, int mode); + +//Used as extradata +typedef struct avpriv_codec2_header { + uint8_t magic[3]; + uint8_t version_major; + uint8_t version_minor; + uint8_t mode; + uint8_t flags; +} avpriv_codec2_header; + +//Used in codec2raw demuxer and libcodec2 encoder to make up .c2 headers +static inline avpriv_codec2_header avpriv_codec2_make_header(int mode) { + avpriv_codec2_header header = { + .magic = {0xC0, 0xDE, 0xC2}, + //version 0.8 as of 2017-08-02 (r3345) + .version_major = 0, + .version_minor = 8, + .mode = mode, + .flags = 0, + }; + return header; +} + +#endif /* AVCODEC_CODEC2UTILS_H */ diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 6f43b68b83..b5f5c2d9d8 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2657,6 +2657,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .props = AV_CODEC_PROP_LOSSY, }, { + .id = AV_CODEC_ID_CODEC2, + .type = AVMEDIA_TYPE_AUDIO, + .name = "codec2", + .long_name = NULL_IF_CONFIG_SMALL("codec2 (very low bitrate speech codec)"), + .props = AV_CODEC_PROP_LOSSY, + }, + { .id = AV_CODEC_ID_G723_1, .type = AVMEDIA_TYPE_AUDIO, .name = "g723_1", diff --git a/libavcodec/libcodec2.c b/libavcodec/libcodec2.c new file mode 100644 index 0000000000..69b8415e7a --- /dev/null +++ b/libavcodec/libcodec2.c @@ -0,0 +1,199 @@ +/* + * codec2 encoder/decoder using libcodec2 + * Copyright (c) 2017 Tomas Härdin + * + * 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 +#include "avcodec.h" +#include "libavutil/opt.h" +#include "internal.h" +#include "codec2utils.h" + +typedef struct { + const AVClass *class; + struct CODEC2 *codec; + char *mode; +} LibCodec2Context; + +static const AVOption options[] = { + //not AV_OPT_FLAG_DECODING_PARAM since mode should come from the demuxer + //1300 (aka FreeDV 1600) is the most common mode on-the-air, default to it here as well + { "mode", "codec2 mode", offsetof(LibCodec2Context, mode), AV_OPT_TYPE_STRING, {.str = "1300"}, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { NULL }, +}; + +static const AVClass codec2_class = { + .class_name = "libcodec2", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static av_cold int libcodec2_init_common(AVCodecContext *avctx) +{ + LibCodec2Context *c2 = avctx->priv_data; + int mode; + + //take -mode if set + if (c2->mode) { + mode = avpriv_codec2_mode_from_str(avctx, c2->mode); + if (mode < 0) { + return mode; + } + + avctx->extradata = av_mallocz(sizeof(avpriv_codec2_header) + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) { + return AVERROR(ENOMEM); + } + + avctx->extradata_size = sizeof(avpriv_codec2_header); + *((avpriv_codec2_header*)avctx->extradata) = avpriv_codec2_make_header(mode); + } else { + if (avctx->extradata_size != sizeof(avpriv_codec2_header)) { + av_log(avctx, AV_LOG_ERROR, "must have exactly %i bytes of extradata (got %i)\n", + (int)sizeof(avpriv_codec2_header), avctx->extradata_size); + return AVERROR_INVALIDDATA; + } + + mode = ((avpriv_codec2_header*)avctx->extradata)->mode; + } + + c2->codec = codec2_create(mode); + if (!c2->codec) { + //it's also possible that the mode is wrong, + //but we can't tell for sure with the current API + return AVERROR(ENOMEM); + } + + avctx->frame_size = codec2_samples_per_frame(c2->codec); + avctx->block_align = (codec2_bits_per_frame(c2->codec) + 7) / 8; + codec2_set_natural_or_gray(c2->codec, 1); + + return 0; +} + +static av_cold int libcodec2_init_decoder(AVCodecContext *avctx) +{ + avctx->sample_rate = 8000; + avctx->channels = 1; + avctx->sample_fmt = AV_SAMPLE_FMT_S16; + avctx->channel_layout = AV_CH_LAYOUT_MONO; + + return libcodec2_init_common(avctx); +} + +static av_cold int libcodec2_init_encoder(AVCodecContext *avctx) +{ + //will need to be smarter once we get wideband support + if (avctx->sample_rate != 8000 || + avctx->channels != 1 || + avctx->sample_fmt != AV_SAMPLE_FMT_S16) { + av_log(avctx, AV_LOG_ERROR, "only 8 kHz 16-bit mono allowed\n"); + return AVERROR(EINVAL); + } + + return libcodec2_init_common(avctx); +} + +static av_cold int libcodec2_close(AVCodecContext *avctx) +{ + LibCodec2Context *c2 = avctx->priv_data; + + codec2_destroy(c2->codec); + return 0; +} + +static int libcodec2_decode(AVCodecContext *avctx, void *data, + int *got_frame_ptr, AVPacket *pkt) +{ + LibCodec2Context *c2 = avctx->priv_data; + AVFrame *frame = data; + int ret, nframes, i; + uint8_t *input; + int16_t *output; + + nframes = pkt->size / avctx->block_align; + frame->nb_samples = avctx->frame_size * nframes; + + ret = ff_get_buffer(avctx, frame, 0); + if (ret < 0) { + return ret; + } + + input = pkt->data; + output = (int16_t *)frame->data[0]; + + for (i = 0; i < nframes; i++) { + codec2_decode(c2->codec, output, input); + input += avctx->block_align; + output += avctx->frame_size; + } + + *got_frame_ptr = nframes > 0; + return nframes * avctx->block_align; +} + +static int libcodec2_encode(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet_ptr) +{ + LibCodec2Context *c2 = avctx->priv_data; + int16_t *samples = (int16_t *)frame->data[0]; + + int ret = ff_alloc_packet2(avctx, avpkt, avctx->block_align, 0); + if (ret < 0) { + return ret; + } + + codec2_encode(c2->codec, avpkt->data, samples); + *got_packet_ptr = 1; + + return 0; +} + +AVCodec ff_libcodec2_decoder = { + .name = "libcodec2", + .long_name = NULL_IF_CONFIG_SMALL("codec2 decoder using libcodec2"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_CODEC2, + .priv_data_size = sizeof(LibCodec2Context), + .init = libcodec2_init_decoder, + .close = libcodec2_close, + .decode = libcodec2_decode, + .capabilities = 0, + .supported_samplerates = (const int[]){ 8000, 0 }, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, + .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_MONO, 0 }, + .priv_class = &codec2_class, +}; + +AVCodec ff_libcodec2_encoder = { + .name = "libcodec2", + .long_name = NULL_IF_CONFIG_SMALL("codec2 encoder using libcodec2"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_CODEC2, + .priv_data_size = sizeof(LibCodec2Context), + .init = libcodec2_init_encoder, + .close = libcodec2_close, + .encode2 = libcodec2_encode, + .capabilities = 0, + .supported_samplerates = (const int[]){ 8000, 0 }, + .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, + .channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_MONO, 0 }, + .priv_class = &codec2_class, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 02c4f41800..7473000579 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 57 -#define LIBAVCODEC_VERSION_MINOR 102 +#define LIBAVCODEC_VERSION_MINOR 103 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ diff --git a/libavformat/Makefile b/libavformat/Makefile index b0ef82cdd4..9aa3466161 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -128,6 +128,10 @@ OBJS-$(CONFIG_CAVSVIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_CDG_DEMUXER) += cdg.o OBJS-$(CONFIG_CDXL_DEMUXER) += cdxl.o OBJS-$(CONFIG_CINE_DEMUXER) += cinedec.o +OBJS-$(CONFIG_CODEC2_DEMUXER) += ../libavcodec/codec2utils.o codec2.o rawdec.o pcm.o +OBJS-$(CONFIG_CODEC2_MUXER) += ../libavcodec/codec2utils.o codec2.o rawenc.o +OBJS-$(CONFIG_CODEC2RAW_DEMUXER) += ../libavcodec/codec2utils.o codec2.o rawdec.o pcm.o +OBJS-$(CONFIG_CODEC2RAW_MUXER) += rawenc.o OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o OBJS-$(CONFIG_CRC_MUXER) += crcenc.o OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 1ebc14231c..26f0f1eccd 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -94,6 +94,8 @@ static void register_all(void) REGISTER_DEMUXER (CDG, cdg); REGISTER_DEMUXER (CDXL, cdxl); REGISTER_DEMUXER (CINE, cine); + REGISTER_MUXDEMUX(CODEC2, codec2); + REGISTER_MUXDEMUX(CODEC2RAW, codec2raw); REGISTER_DEMUXER (CONCAT, concat); REGISTER_MUXER (CRC, crc); REGISTER_MUXER (DASH, dash); diff --git a/libavformat/codec2.c b/libavformat/codec2.c new file mode 100644 index 0000000000..e9139e56e8 --- /dev/null +++ b/libavformat/codec2.c @@ -0,0 +1,285 @@ +/* + * codec2 raw demuxer + * Copyright (c) 2017 Tomas Härdin + * + * 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/codec2utils.h" +#include "avio_internal.h" +#include "avformat.h" +#include "internal.h" +#include "rawdec.h" +#include "rawenc.h" +#include "pcm.h" + +//the lowest version we should ever run across is 0.8 +//we may run across later versions as the format evolves +#define EXPECTED_CODEC2_MAJOR_VERSION 0 +#define EXPECTED_CODEC2_MINOR_VERSION 8 + +typedef struct { + const AVClass *class; + char *mode; +} Codec2Context; + +//check for 0xC0DEC2, return non-zero if it doesn't match +static int check_magic(uint8_t *ptr) { + return ptr[0] != 0xC0 || ptr[1] != 0xDE || ptr[2] != 0xC2; +} + +static int codec2_probe(AVProbeData *p) +{ + int score; + + //must be at least 7 bytes and start wih 0xC0DEC2 + if (p->buf_size < sizeof(avpriv_codec2_header) || check_magic(p->buf)) { + return 0; + } + + //no .c2 files prior to 0.8 + if (p->buf[3] == 0 && p->buf[4] < 8) { + return 0; + } + + //give a poor score if major version doesn't match + //this allows such files to be detected at least, even if we can't do much with them + if (p->buf[3] != EXPECTED_CODEC2_MAJOR_VERSION) { + return AVPROBE_SCORE_MAX/10; + } + + //if the minor version is known, no unknown mode is used and no flags are set then -> max score, + //else penalize 20% for each byte outside of expectations + //this way if only the first four bytes are OK then we're slightly less than AVPROBE_SCORE_MAX/2 + score = AVPROBE_SCORE_MAX; + if (p->buf[4] > EXPECTED_CODEC2_MINOR_VERSION) score -= AVPROBE_SCORE_MAX/5; + if (p->buf[5] > AVPRIV_CODEC2_MAX_KNOWN_MODE) score -= AVPROBE_SCORE_MAX/5; + if (p->buf[6] != 0) score -= AVPROBE_SCORE_MAX/5; + return score; +} + +static int codec2_read_header_common(AVFormatContext *s, AVStream *st) +{ + int mode = ((avpriv_codec2_header*)st->codecpar->extradata)->mode; + + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_CODEC2; + st->codecpar->sample_rate = 8000; + st->codecpar->channels = 1; + st->codecpar->format = AV_SAMPLE_FMT_S16; + st->codecpar->channel_layout = AV_CH_LAYOUT_MONO; + st->codecpar->bit_rate = avpriv_codec2_mode_bit_rate(s, mode); + st->codecpar->frame_size = avpriv_codec2_mode_frame_size(s, mode); + st->codecpar->block_align = avpriv_codec2_mode_block_align(s, mode); + + if (st->codecpar->bit_rate <= 0 || + st->codecpar->frame_size <= 0 || + st->codecpar->block_align <= 0) { + return AVERROR(EINVAL); + } + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + + //replicating estimate_timings_from_bit_rate() in utils.c to avoid warnings + if (s->pb && st->codecpar->bit_rate > 0) { + int64_t filesize = avio_size(s->pb); + if (filesize > s->internal->data_offset) { + filesize -= s->internal->data_offset; + st->duration = av_rescale(8 * filesize, + st->time_base.den, + st->codecpar->bit_rate * (int64_t) st->time_base.num); + } + } + + return 0; +} + +static int codec2_read_header(AVFormatContext *s) +{ + avpriv_codec2_header *header; + AVStream *st = avformat_new_stream(s, NULL); + int ret; + + if (!st) { + return AVERROR(ENOMEM); + } + + ret = ff_alloc_extradata(st->codecpar, sizeof(avpriv_codec2_header)); + if (ret) { + return ret; + } + + s->internal->data_offset = sizeof(avpriv_codec2_header); + avio_read(s->pb, st->codecpar->extradata, sizeof(avpriv_codec2_header)); + header = (avpriv_codec2_header*)st->codecpar->extradata; + + if (check_magic(header->magic)) { + av_log(s, AV_LOG_ERROR, "not a .c2 file\n"); + return AVERROR_INVALIDDATA; + } + + if (header->version_major != EXPECTED_CODEC2_MAJOR_VERSION) { + avpriv_report_missing_feature(s, "Major version %i", header->version_major); + return AVERROR_PATCHWELCOME; + } + + return codec2_read_header_common(s, st); +} + +static int codec2_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVStream *st = s->streams[0]; + int ret, size, n, block_align, frame_size; + + block_align = st->codecpar->block_align; + frame_size = st->codecpar->frame_size; + + if (block_align <= 0 || frame_size <= 0) { + return AVERROR(EINVAL); + } + + //Read roughly 0.1 seconds worth of data. + n = st->codecpar->bit_rate / ((int)(8/0.1) * block_align) + 1; + size = n * block_align; + + //try to read desired number of bytes, recompute n from to actual number of bytes read + ret = av_get_packet(s->pb, pkt, size); + if (ret < 0) { + return ret; + } + + //only set duration - compute_pkt_fields() and ff_pcm_read_seek() takes care of everything else + //tested by spamming the seek functionality in ffplay + n = ret / block_align; + pkt->duration = n * frame_size; + + return ret; +} + +static int codec2_write_header(AVFormatContext *s) +{ + AVStream *st; + + if (s->nb_streams != 1 || s->streams[0]->codecpar->codec_id != AV_CODEC_ID_CODEC2) { + av_log(s, AV_LOG_ERROR, ".c2 files must have exactly one codec2 stream\n"); + return AVERROR(EINVAL); + } + + st = s->streams[0]; + + if (st->codecpar->extradata_size != sizeof(avpriv_codec2_header)) { + av_log(s, AV_LOG_ERROR, ".c2 files require exactly %i bytes of extradata (got %i)\n", + (int)sizeof(avpriv_codec2_header), st->codecpar->extradata_size); + return AVERROR(EINVAL); + } + + avio_write(s->pb, st->codecpar->extradata, sizeof(avpriv_codec2_header)); + + return 0; +} + +static int codec2raw_read_header(AVFormatContext *s) +{ + Codec2Context *c2 = s->priv_data; + int ret, mode; + + AVStream *st = avformat_new_stream(s, NULL); + if (!st) { + return AVERROR(ENOMEM); + } + + ret = ff_alloc_extradata(st->codecpar, sizeof(avpriv_codec2_header)); + if (ret) { + return ret; + } + + mode = avpriv_codec2_mode_from_str(s, c2->mode); + if (mode < 0) { + return mode; + } + + s->internal->data_offset = 0; + *((avpriv_codec2_header*)st->codecpar->extradata) = avpriv_codec2_make_header(mode); + + return codec2_read_header_common(s, st); +} + +static const AVOption options[] = { + { "mode", "codec2 mode", offsetof(Codec2Context, mode), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM}, + { NULL }, +}; + +static const AVClass codec2_class = { + .class_name = "codec2", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +static const AVClass codec2raw_class = { + .class_name = "codec2raw", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +#if CONFIG_CODEC2_DEMUXER +AVInputFormat ff_codec2_demuxer = { + .name = "codec2", + .long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 demuxer"), + .priv_data_size = sizeof(Codec2Context), + .extensions = "c2", + .read_probe = codec2_probe, + .read_header = codec2_read_header, + .read_packet = codec2_read_packet, + .read_seek = ff_pcm_read_seek, + .flags = AVFMT_GENERIC_INDEX, + .raw_codec_id = AV_CODEC_ID_CODEC2, + .priv_class = &codec2_class, +}; +#endif + +#if CONFIG_CODEC2_MUXER +AVOutputFormat ff_codec2_muxer = { + .name = "codec2", + .long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 muxer"), + .priv_data_size = sizeof(Codec2Context), + .extensions = "c2", + .audio_codec = AV_CODEC_ID_CODEC2, + .video_codec = AV_CODEC_ID_NONE, + .write_header = codec2_write_header, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, + .priv_class = &codec2_class, +}; +#endif + +#if CONFIG_CODEC2RAW_DEMUXER +AVInputFormat ff_codec2raw_demuxer = { + .name = "codec2raw", + .long_name = NULL_IF_CONFIG_SMALL("raw codec2 demuxer"), + .priv_data_size = sizeof(Codec2Context), + .read_header = codec2raw_read_header, + .read_packet = codec2_read_packet, + .read_seek = ff_pcm_read_seek, + .flags = AVFMT_GENERIC_INDEX, + .raw_codec_id = AV_CODEC_ID_CODEC2, + .priv_class = &codec2raw_class, +}; +#endif diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c index 26baa850e1..6ef22f21e3 100644 --- a/libavformat/rawenc.c +++ b/libavformat/rawenc.c @@ -104,6 +104,19 @@ AVOutputFormat ff_cavsvideo_muxer = { }; #endif +#if CONFIG_CODEC2RAW_MUXER +AVOutputFormat ff_codec2raw_muxer = { + .name = "codec2raw", + .long_name = NULL_IF_CONFIG_SMALL("raw codec2 muxer"), + .audio_codec = AV_CODEC_ID_CODEC2, + .video_codec = AV_CODEC_ID_NONE, + .write_header = force_one_stream, + .write_packet = ff_raw_write_packet, + .flags = AVFMT_NOTIMESTAMPS, +}; +#endif + + #if CONFIG_DATA_MUXER AVOutputFormat ff_data_muxer = { .name = "data", diff --git a/libavformat/utils.c b/libavformat/utils.c index 38d247c6cd..3558e700ac 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c @@ -898,6 +898,7 @@ static int determinable_frame_size(AVCodecContext *avctx) case AV_CODEC_ID_MP1: case AV_CODEC_ID_MP2: case AV_CODEC_ID_MP3: + case AV_CODEC_ID_CODEC2: return 1; } diff --git a/libavformat/version.h b/libavformat/version.h index 48b81f2e48..a8cf4c158e 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 57 -#define LIBAVFORMAT_VERSION_MINOR 76 +#define LIBAVFORMAT_VERSION_MINOR 77 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- 2.13.3