diff mbox

[FFmpeg-devel,WIP] libcodec2 wrapper + de/muxer in FFmpeg

Message ID 1501867247.24518.3.camel@acc.umu.se
State Superseded
Headers show

Commit Message

Tomas Härdin Aug. 4, 2017, 5:20 p.m. UTC
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 <codec2/codec2.h> 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
diff mbox

Patch

From 2aef2d364fd93059ac9092f4675192749a5e91c0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= <tjoppen@acc.umu.se>
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 <next>:
 - 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 <string.h>
+#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 <stdint.h>
+
+#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 <codec2/codec2.h>
+#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