LCOV - code coverage report
Current view: top level - libavformat - codec2.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 3 83 3.6 %
Date: 2018-05-20 11:54:08 Functions: 1 6 16.7 %

          Line data    Source code
       1             : /*
       2             :  * codec2 muxer and demuxers
       3             :  * Copyright (c) 2017 Tomas Härdin
       4             :  *
       5             :  * This file is part of FFmpeg.
       6             :  *
       7             :  * FFmpeg is free software; you can redistribute it and/or
       8             :  * modify it under the terms of the GNU Lesser General Public
       9             :  * License as published by the Free Software Foundation; either
      10             :  * version 2.1 of the License, or (at your option) any later version.
      11             :  *
      12             :  * FFmpeg is distributed in the hope that it will be useful,
      13             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * Lesser General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU Lesser General Public
      18             :  * License along with FFmpeg; if not, write to the Free Software
      19             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      20             :  */
      21             : 
      22             : #include <memory.h>
      23             : #include "libavcodec/codec2utils.h"
      24             : #include "libavutil/intreadwrite.h"
      25             : #include "avio_internal.h"
      26             : #include "avformat.h"
      27             : #include "internal.h"
      28             : #include "rawdec.h"
      29             : #include "rawenc.h"
      30             : #include "pcm.h"
      31             : 
      32             : #define AVPRIV_CODEC2_HEADER_SIZE 7
      33             : #define AVPRIV_CODEC2_MAGIC       0xC0DEC2
      34             : 
      35             : //the lowest version we should ever run across is 0.8
      36             : //we may run across later versions as the format evolves
      37             : #define EXPECTED_CODEC2_MAJOR_VERSION 0
      38             : #define EXPECTED_CODEC2_MINOR_VERSION 8
      39             : 
      40             : typedef struct {
      41             :     const AVClass *class;
      42             :     int mode;
      43             :     int frames_per_packet;
      44             : } Codec2Context;
      45             : 
      46        6217 : static int codec2_probe(AVProbeData *p)
      47             : {
      48             :     //must start wih C0 DE C2
      49        6217 :     if (AV_RB24(p->buf) != AVPRIV_CODEC2_MAGIC) {
      50        6217 :         return 0;
      51             :     }
      52             : 
      53             :     //no .c2 files prior to 0.8
      54             :     //be strict about major version while we're at it
      55           0 :     if (p->buf[3] != EXPECTED_CODEC2_MAJOR_VERSION ||
      56           0 :         p->buf[4] <  EXPECTED_CODEC2_MINOR_VERSION) {
      57           0 :         return 0;
      58             :     }
      59             : 
      60             :     //32 bits of identification -> low score
      61           0 :     return AVPROBE_SCORE_EXTENSION + 1;
      62             : }
      63             : 
      64           0 : static int codec2_read_header_common(AVFormatContext *s, AVStream *st)
      65             : {
      66           0 :     int mode = avpriv_codec2_mode_from_extradata(st->codecpar->extradata);
      67             : 
      68           0 :     st->codecpar->codec_type        = AVMEDIA_TYPE_AUDIO;
      69           0 :     st->codecpar->codec_id          = AV_CODEC_ID_CODEC2;
      70           0 :     st->codecpar->sample_rate       = 8000;
      71           0 :     st->codecpar->channels          = 1;
      72           0 :     st->codecpar->format            = AV_SAMPLE_FMT_S16;
      73           0 :     st->codecpar->channel_layout    = AV_CH_LAYOUT_MONO;
      74           0 :     st->codecpar->bit_rate          = avpriv_codec2_mode_bit_rate(s, mode);
      75           0 :     st->codecpar->frame_size        = avpriv_codec2_mode_frame_size(s, mode);
      76           0 :     st->codecpar->block_align       = avpriv_codec2_mode_block_align(s, mode);
      77             : 
      78           0 :     if (st->codecpar->bit_rate <= 0 ||
      79           0 :         st->codecpar->frame_size <= 0 ||
      80           0 :         st->codecpar->block_align <= 0) {
      81           0 :         return AVERROR_INVALIDDATA;
      82             :     }
      83             : 
      84           0 :     avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate);
      85             : 
      86           0 :     return 0;
      87             : }
      88             : 
      89           0 : static int codec2_read_header(AVFormatContext *s)
      90             : {
      91           0 :     AVStream *st = avformat_new_stream(s, NULL);
      92             :     int ret, version;
      93             : 
      94           0 :     if (!st) {
      95           0 :         return AVERROR(ENOMEM);
      96             :     }
      97             : 
      98           0 :     if (avio_rb24(s->pb) != AVPRIV_CODEC2_MAGIC) {
      99           0 :         av_log(s, AV_LOG_ERROR, "not a .c2 file\n");
     100           0 :         return AVERROR_INVALIDDATA;
     101             :     }
     102             : 
     103           0 :     ret = ff_alloc_extradata(st->codecpar, AVPRIV_CODEC2_EXTRADATA_SIZE);
     104           0 :     if (ret) {
     105           0 :         return ret;
     106             :     }
     107             : 
     108           0 :     ret = ffio_read_size(s->pb, st->codecpar->extradata, AVPRIV_CODEC2_EXTRADATA_SIZE);
     109           0 :     if (ret < 0) {
     110           0 :         return ret;
     111             :     }
     112             : 
     113           0 :     version = avpriv_codec2_version_from_extradata(st->codecpar->extradata);
     114           0 :     if ((version >> 8) != EXPECTED_CODEC2_MAJOR_VERSION) {
     115           0 :         avpriv_report_missing_feature(s, "Major version %i", version >> 8);
     116           0 :         return AVERROR_PATCHWELCOME;
     117             :     }
     118             : 
     119           0 :     s->internal->data_offset = AVPRIV_CODEC2_HEADER_SIZE;
     120             : 
     121           0 :     return codec2_read_header_common(s, st);
     122             : }
     123             : 
     124           0 : static int codec2_read_packet(AVFormatContext *s, AVPacket *pkt)
     125             : {
     126           0 :     Codec2Context *c2 = s->priv_data;
     127           0 :     AVStream *st = s->streams[0];
     128             :     int ret, size, n, block_align, frame_size;
     129             : 
     130           0 :     block_align = st->codecpar->block_align;
     131           0 :     frame_size  = st->codecpar->frame_size;
     132             : 
     133           0 :     if (block_align <= 0 || frame_size <= 0 || c2->frames_per_packet <= 0) {
     134           0 :         return AVERROR(EINVAL);
     135             :     }
     136             : 
     137             :     //try to read desired number of frames, compute n from to actual number of bytes read
     138           0 :     size = c2->frames_per_packet * block_align;
     139           0 :     ret = av_get_packet(s->pb, pkt, size);
     140           0 :     if (ret < 0) {
     141           0 :         return ret;
     142             :     }
     143             : 
     144             :     //only set duration - compute_pkt_fields() and ff_pcm_read_seek() takes care of everything else
     145             :     //tested by spamming the seek functionality in ffplay
     146           0 :     n = ret / block_align;
     147           0 :     pkt->duration = n * frame_size;
     148             : 
     149           0 :     return ret;
     150             : }
     151             : 
     152           0 : static int codec2_write_header(AVFormatContext *s)
     153             : {
     154             :     AVStream *st;
     155             : 
     156           0 :     if (s->nb_streams != 1 || s->streams[0]->codecpar->codec_id != AV_CODEC_ID_CODEC2) {
     157           0 :         av_log(s, AV_LOG_ERROR, ".c2 files must have exactly one codec2 stream\n");
     158           0 :         return AVERROR(EINVAL);
     159             :     }
     160             : 
     161           0 :     st = s->streams[0];
     162             : 
     163           0 :     if (st->codecpar->extradata_size != AVPRIV_CODEC2_EXTRADATA_SIZE) {
     164           0 :         av_log(s, AV_LOG_ERROR, ".c2 files require exactly %i bytes of extradata (got %i)\n",
     165           0 :                AVPRIV_CODEC2_EXTRADATA_SIZE, st->codecpar->extradata_size);
     166           0 :         return AVERROR(EINVAL);
     167             :     }
     168             : 
     169           0 :     avio_wb24(s->pb, AVPRIV_CODEC2_MAGIC);
     170           0 :     avio_write(s->pb, st->codecpar->extradata, AVPRIV_CODEC2_EXTRADATA_SIZE);
     171             : 
     172           0 :     return 0;
     173             : }
     174             : 
     175           0 : static int codec2raw_read_header(AVFormatContext *s)
     176             : {
     177           0 :     Codec2Context *c2 = s->priv_data;
     178             :     AVStream *st;
     179             :     int ret;
     180             : 
     181           0 :     if (c2->mode < 0) {
     182             :         //FIXME: using a default value of -1 for mandatory options is an incredibly ugly hack
     183           0 :         av_log(s, AV_LOG_ERROR, "-mode must be set in order to make sense of raw codec2 files\n");
     184           0 :         return AVERROR(EINVAL);
     185             :     }
     186             : 
     187           0 :     st = avformat_new_stream(s, NULL);
     188           0 :     if (!st) {
     189           0 :         return AVERROR(ENOMEM);
     190             :     }
     191             : 
     192           0 :     ret = ff_alloc_extradata(st->codecpar, AVPRIV_CODEC2_EXTRADATA_SIZE);
     193           0 :     if (ret) {
     194           0 :         return ret;
     195             :     }
     196             : 
     197           0 :     s->internal->data_offset = 0;
     198           0 :     avpriv_codec2_make_extradata(st->codecpar->extradata, c2->mode);
     199             : 
     200           0 :     return codec2_read_header_common(s, st);
     201             : }
     202             : 
     203             : //transcoding report2074.c2 to wav went from 7.391s to 5.322s with -frames_per_packet 1000 compared to default, same sha1sum
     204             : #define FRAMES_PER_PACKET \
     205             :     { "frames_per_packet", "Number of frames to read at a time. Higher = faster decoding, lower granularity", \
     206             :       offsetof(Codec2Context, frames_per_packet), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM}
     207             : 
     208             : static const AVOption codec2_options[] = {
     209             :     FRAMES_PER_PACKET,
     210             :     { NULL },
     211             : };
     212             : 
     213             : static const AVOption codec2raw_options[] = {
     214             :     AVPRIV_CODEC2_AVOPTIONS("codec2 mode [mandatory]", Codec2Context, -1, -1, AV_OPT_FLAG_DECODING_PARAM),
     215             :     FRAMES_PER_PACKET,
     216             :     { NULL },
     217             : };
     218             : 
     219             : static const AVClass codec2_mux_class = {
     220             :     .class_name = "codec2 muxer",
     221             :     .item_name  = av_default_item_name,
     222             :     .version    = LIBAVUTIL_VERSION_INT,
     223             :     .category   = AV_CLASS_CATEGORY_DEMUXER,
     224             : };
     225             : 
     226             : static const AVClass codec2_demux_class = {
     227             :     .class_name = "codec2 demuxer",
     228             :     .item_name  = av_default_item_name,
     229             :     .option     = codec2_options,
     230             :     .version    = LIBAVUTIL_VERSION_INT,
     231             :     .category   = AV_CLASS_CATEGORY_DEMUXER,
     232             : };
     233             : 
     234             : static const AVClass codec2raw_demux_class = {
     235             :     .class_name = "codec2raw demuxer",
     236             :     .item_name  = av_default_item_name,
     237             :     .option     = codec2raw_options,
     238             :     .version    = LIBAVUTIL_VERSION_INT,
     239             :     .category   = AV_CLASS_CATEGORY_DEMUXER,
     240             : };
     241             : 
     242             : #if CONFIG_CODEC2_DEMUXER
     243             : AVInputFormat ff_codec2_demuxer = {
     244             :     .name           = "codec2",
     245             :     .long_name      = NULL_IF_CONFIG_SMALL("codec2 .c2 demuxer"),
     246             :     .priv_data_size = sizeof(Codec2Context),
     247             :     .extensions     = "c2",
     248             :     .read_probe     = codec2_probe,
     249             :     .read_header    = codec2_read_header,
     250             :     .read_packet    = codec2_read_packet,
     251             :     .read_seek      = ff_pcm_read_seek,
     252             :     .flags          = AVFMT_GENERIC_INDEX,
     253             :     .raw_codec_id   = AV_CODEC_ID_CODEC2,
     254             :     .priv_class     = &codec2_demux_class,
     255             : };
     256             : #endif
     257             : 
     258             : #if CONFIG_CODEC2_MUXER
     259             : AVOutputFormat ff_codec2_muxer = {
     260             :     .name           = "codec2",
     261             :     .long_name      = NULL_IF_CONFIG_SMALL("codec2 .c2 muxer"),
     262             :     .priv_data_size = sizeof(Codec2Context),
     263             :     .extensions     = "c2",
     264             :     .audio_codec    = AV_CODEC_ID_CODEC2,
     265             :     .video_codec    = AV_CODEC_ID_NONE,
     266             :     .write_header   = codec2_write_header,
     267             :     .write_packet   = ff_raw_write_packet,
     268             :     .flags          = AVFMT_NOTIMESTAMPS,
     269             :     .priv_class     = &codec2_mux_class,
     270             : };
     271             : #endif
     272             : 
     273             : #if CONFIG_CODEC2RAW_DEMUXER
     274             : AVInputFormat ff_codec2raw_demuxer = {
     275             :     .name           = "codec2raw",
     276             :     .long_name      = NULL_IF_CONFIG_SMALL("raw codec2 demuxer"),
     277             :     .priv_data_size = sizeof(Codec2Context),
     278             :     .read_header    = codec2raw_read_header,
     279             :     .read_packet    = codec2_read_packet,
     280             :     .read_seek      = ff_pcm_read_seek,
     281             :     .flags          = AVFMT_GENERIC_INDEX,
     282             :     .raw_codec_id   = AV_CODEC_ID_CODEC2,
     283             :     .priv_class     = &codec2raw_demux_class,
     284             : };
     285             : #endif

Generated by: LCOV version 1.13