[FFmpeg-devel] [PATCH] IRCAM Sound Format demuxer

Daniel Verkamp daniel
Tue Apr 20 20:03:33 CEST 2010


On Tue, Apr 20, 2010 at 1:02 AM, Jai Menon <jmenon86 at gmail.com> wrote:
> On Tue, Apr 20, 2010 at 9:29 AM, Daniel Verkamp <daniel at drv.nu> wrote:
>> ---
>> ?Changelog ? ? ? ? ? ? ? ?| ? ?1 +
>> ?doc/general.texi ? ? ? ? | ? ?1 +
>> ?libavformat/Makefile ? ? | ? ?1 +
>> ?libavformat/allformats.c | ? ?1 +
>> ?libavformat/avformat.h ? | ? ?2 +-
>> ?libavformat/ircamsf.c ? ?| ?200 ++++++++++++++++++++++++++++++++++++++++++++++
>> ?6 files changed, 205 insertions(+), 1 deletions(-)
>
> [...]
>
>> ?create mode 100644 libavformat/ircamsf.c
>> diff --git a/libavformat/ircamsf.c b/libavformat/ircamsf.c
>> new file mode 100644
>> index 0000000..3ce97f6
>> --- /dev/null
>> +++ b/libavformat/ircamsf.c
>> @@ -0,0 +1,200 @@
>> +/*
>> + * IRCAM SF demuxer
>> + * Copyright (c) 2010 Daniel Verkamp
>> + *
>> + * 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
>> + */
>> +
>> +/**
>> + * IRCAM SF demuxer
>> + * @file libavformat/ircamsf.c
>> + * @author Daniel Verkamp
>> + * @sa http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/IRCAM/IRCAM.html
>> + */
>> +
>> +#include "avformat.h"
>> +#include "raw.h"
>> +#include "riff.h"
>> +#include "libavutil/intreadwrite.h"
>> +
>> +static const AVCodecTag generic_codec_tags[] = {
>> + ? ?{ CODEC_ID_PCM_S8, ? ?0x00001 },
>> + ? ?{ CODEC_ID_PCM_ALAW, ?0x10001 },
>> + ? ?{ CODEC_ID_PCM_MULAW, 0x20001 },
>> +};
>> +
>> +static const AVCodecTag le_codec_tags[] = {
>> + ? ?{ CODEC_ID_PCM_S16LE, 0x00002 },
>> + ? ?{ CODEC_ID_PCM_S24LE, 0x00003 },
>> + ? ?{ CODEC_ID_PCM_S32LE, 0x40004 },
>> + ? ?{ CODEC_ID_PCM_F32LE, 0x00004 },
>> + ? ?{ CODEC_ID_PCM_F64LE, 0x00008 },
>> +};
>> +
>> +static const AVCodecTag be_codec_tags[] = {
>> + ? ?{ CODEC_ID_PCM_S16BE, 0x00002 },
>> + ? ?{ CODEC_ID_PCM_S24BE, 0x00003 },
>> + ? ?{ CODEC_ID_PCM_S32BE, 0x40004 },
>> + ? ?{ CODEC_ID_PCM_F32BE, 0x00004 },
>> + ? ?{ CODEC_ID_PCM_F64BE, 0x00008 },
>> +};
>
> an additional CODEC_ID_NONE,0 is required in these tables.
>

Right, silly mistake, fixed.

> [...]
>
>> +static int identify(uint32_t tag)
>> +{
>> + ? ?int i;
>> +
>> + ? ?for (i = 0; i < sizeof(be_tags) / sizeof(*be_tags); i++)
>
> FF_ARRAY_ELEMS here and below
>

Aha, I knew that existed somewhere, but I couldn't find it...

> [...]
>
>> +static int probe(AVProbeData *p)
>> +{
>> + ? ?unsigned char *buf = p->buf;
>> + ? ?if (p->buf_size <= 16)
>> + ? ? ? ?return 0;
>> +
>> + ? ?if (identify(AV_RB32(buf)) < 0)
>> + ? ? ? ?return 0;
>> +
>> + ? ?if (!AV_RB32(buf + 4) || !AV_RB32(buf + 8) || !AV_RB32(buf + 12))
>> + ? ? ? ?return 0;
>> +
>> + ? ?return AVPROBE_SCORE_MAX;
>
> Could be fragile, is there anything else we could check? stream
> parameters perhaps?
>

I added a slightly better check here; anything more would be heuristics.

>> +}
>> +
>> +static int read_header(AVFormatContext *s, AVFormatParameters *ap)
>> +{
>> + ? ?ByteIOContext *pb = s->pb;
>> + ? ?AVStream *st;
>> + ? ?AVCodecContext *dec;
>> + ? ?const AVCodecTag *tags;
>> + ? ?uint32_t tag;
>> + ? ?int be, bits_per_frame, chunk, sz;
>> + ? ?char *comment;
>> +
>> + ? ?be = identify(get_be32(pb));
>> + ? ?if (be < 0)
>> + ? ? ? ?return AVERROR_INVALIDDATA;
>> +
>> + ? ?st = av_new_stream(s, 0);
>> + ? ?if (!st)
>> + ? ? ? ?return AVERROR(ENOMEM);
>> +
>> + ? ?dec = st->codec;
>> + ? ?dec->codec_type = AVMEDIA_TYPE_AUDIO;
>> +
>> + ? ?if (be) {
>> + ? ? ? ?dec->sample_rate = av_int2flt(get_be32(pb));
>> + ? ? ? ?dec->channels ? ?= get_be32(pb);
>> + ? ? ? ?tag ? ? ? ? ? ? ?= get_be32(pb);
>> + ? ? ? ?tags ? ? ? ? ? ? = be_codec_tags;
>> + ? ?} else {
>> + ? ? ? ?dec->sample_rate = av_int2flt(get_le32(pb));
>> + ? ? ? ?dec->channels ? ?= get_le32(pb);
>> + ? ? ? ?tag ? ? ? ? ? ? ?= get_le32(pb);
>> + ? ? ? ?tags ? ? ? ? ? ? = le_codec_tags;
>> + ? ?}
>> +
>> + ? ?if (dec->sample_rate <= 0) {
>> + ? ? ? ?av_log(s, AV_LOG_ERROR, "invalid sample rate %d\n", dec->sample_rate);
>> + ? ? ? ?return AVERROR_INVALIDDATA;
>> + ? ?}
>> +
>> + ? ?dec->codec_id = ff_codec_get_id(tags, tag);
>> + ? ?if (dec->codec_id == CODEC_ID_NONE)
>> + ? ? ? ?dec->codec_id = ff_codec_get_id(generic_codec_tags, tag);
>> +
>> + ? ?av_set_pts_info(st, 64, 1, dec->sample_rate);
>> +
>> + ? ?while (url_ftell(pb) < 1024) {
>> + ? ? ? ?if (be) {
>> + ? ? ? ? ? ?chunk = get_be16(pb);
>> + ? ? ? ? ? ?sz ? ?= get_be16(pb);
>> + ? ? ? ?} else {
>> + ? ? ? ? ? ?chunk = get_le16(pb);
>> + ? ? ? ? ? ?sz ? ?= get_le16(pb);
>> + ? ? ? ?}
>> +
>> + ? ? ? ?if (!chunk) // end chunk
>> + ? ? ? ? ? ?break;
>> +
>> + ? ? ? ?switch (chunk) {
>> + ? ? ? ?case 2: // comment
>> + ? ? ? ? ? ?comment = av_malloc(sz + 1);
>> + ? ? ? ? ? ?if (get_buffer(pb, comment, sz) != sz) {
>> + ? ? ? ? ? ? ? ?av_freep(&comment);
>> + ? ? ? ? ? ? ? ?return AVERROR(EIO);
>> + ? ? ? ? ? ?}
>> + ? ? ? ? ? ?comment[sz] = 0;
>> + ? ? ? ? ? ?av_metadata_set2(&s->metadata, "comment", comment,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? AV_METADATA_DONT_STRDUP_VAL);
>> + ? ? ? ? ? ?break;
>> + ? ? ? ?default:
>> + ? ? ? ? ? ?url_fskip(pb, sz);
>> + ? ? ? ? ? ?break;
>> + ? ? ? ?}
>> + ? ?}
>> +
>> + ? ?url_fseek(pb, 1024, SEEK_SET);
>
> Could this be avoided, by skipping the padding bytes instead?
>

By my reading of the url_fseek() code, this already happens
internally, but I could be wrong.  I specifically made it a seek and
not a skip in case the previous loop overreads into the sample data.

>> +
>> + ? ?dec->bits_per_coded_sample = av_get_bits_per_sample(dec->codec_id);
>> + ? ?bits_per_frame ? = dec->channels * dec->bits_per_coded_sample;
>> + ? ?dec->block_align = bits_per_frame >> 3;
>> + ? ?dec->bit_rate ? ?= bits_per_frame * dec->sample_rate;
>> +
>> + ? ?return 0;
>> +}
>> +
>> +#define NUM_SAMPLES 1024
>> +
>> +static int read_packet(AVFormatContext *s, AVPacket *pkt)
>> +{
>> + ? ?return av_get_packet(s->pb, pkt,
>> + ? ? ? ? ? ? ? ? ? ? ? ? NUM_SAMPLES * s->streams[0]->codec->block_align);
>> +}
>
> looks like raw_read_packet could be used but maybe I'm missing something
>

I suppose so, although it does pts/dts calculations that don't seem
necessary, and it would need to be made non-static.

> --
> Jai Menon

Thanks for the review.
-- Daniel Verkamp
-------------- next part --------------
>From 0ad446191bc9e0b962235a6378245d7666ae4e7f Mon Sep 17 00:00:00 2001
From: Daniel Verkamp <daniel at drv.nu>
Date: Mon, 19 Apr 2010 23:26:19 -0400
Subject: [PATCH] IRCAM Sound Format demuxer

---
 Changelog                |    1 +
 doc/general.texi         |    1 +
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    1 +
 libavformat/avformat.h   |    2 +-
 libavformat/ircamsf.c    |  225 ++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 230 insertions(+), 1 deletions(-)
 create mode 100644 libavformat/ircamsf.c

diff --git a/Changelog b/Changelog
index c454c15..7ad100f 100644
--- a/Changelog
+++ b/Changelog
@@ -70,6 +70,7 @@ version <next>:
 - Psygnosis YOP demuxer and video decoder
 - spectral extension support in the E-AC-3 decoder
 - unsharp video filter
+- IRCAM Sound Format (.sf) demuxer
 
 
 
diff --git a/doc/general.texi b/doc/general.texi
index 1bfde02..8dfb07c 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -107,6 +107,7 @@ library:
     @tab Interchange File Format
 @item Interplay MVE             @tab   @tab X
     @tab Format used in various Interplay computer games.
+ at item IRCAM Sound Format (.sf)  @tab   @tab X
 @item IV8                       @tab   @tab X
     @tab A format generated by IndigoVision 8000 video server.
 @item LMLM4                     @tab   @tab X
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 43a4a2e..e7e1205 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -96,6 +96,7 @@ OBJS-$(CONFIG_IMAGE2PIPE_DEMUXER)        += img2.o
 OBJS-$(CONFIG_IMAGE2PIPE_MUXER)          += img2.o
 OBJS-$(CONFIG_INGENIENT_DEMUXER)         += raw.o
 OBJS-$(CONFIG_IPMOVIE_DEMUXER)           += ipmovie.o
+OBJS-$(CONFIG_IRCAMSF_DEMUXER)           += ircamsf.o raw.o
 OBJS-$(CONFIG_ISS_DEMUXER)               += iss.o
 OBJS-$(CONFIG_IV8_DEMUXER)               += iv8.o
 OBJS-$(CONFIG_LMLM4_DEMUXER)             += lmlm4.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 27dba10..d048549 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -102,6 +102,7 @@ void av_register_all(void)
     REGISTER_DEMUXER  (INGENIENT, ingenient);
     REGISTER_DEMUXER  (IPMOVIE, ipmovie);
     REGISTER_MUXER    (IPOD, ipod);
+    REGISTER_DEMUXER  (IRCAMSF, ircamsf);
     REGISTER_DEMUXER  (ISS, iss);
     REGISTER_DEMUXER  (IV8, iv8);
     REGISTER_DEMUXER  (LMLM4, lmlm4);
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index 5ff08c0..09d176d 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -22,7 +22,7 @@
 #define AVFORMAT_AVFORMAT_H
 
 #define LIBAVFORMAT_VERSION_MAJOR 52
-#define LIBAVFORMAT_VERSION_MINOR 61
+#define LIBAVFORMAT_VERSION_MINOR 62
 #define LIBAVFORMAT_VERSION_MICRO  0
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
diff --git a/libavformat/ircamsf.c b/libavformat/ircamsf.c
new file mode 100644
index 0000000..7892e31
--- /dev/null
+++ b/libavformat/ircamsf.c
@@ -0,0 +1,225 @@
+/*
+ * IRCAM SF demuxer
+ * Copyright (c) 2010 Daniel Verkamp
+ *
+ * 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
+ */
+
+/**
+ * IRCAM SF demuxer
+ * @file libavformat/ircamsf.c
+ * @author Daniel Verkamp
+ * @sa http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/IRCAM/IRCAM.html
+ */
+
+#include "avformat.h"
+#include "raw.h"
+#include "riff.h"
+#include "libavutil/intreadwrite.h"
+
+static const AVCodecTag generic_codec_tags[] = {
+    { CODEC_ID_PCM_S8,    0x00001 },
+    { CODEC_ID_PCM_ALAW,  0x10001 },
+    { CODEC_ID_PCM_MULAW, 0x20001 },
+    { CODEC_ID_NONE,      0       },
+};
+
+static const AVCodecTag le_codec_tags[] = {
+    { CODEC_ID_PCM_S16LE, 0x00002 },
+    { CODEC_ID_PCM_S24LE, 0x00003 },
+    { CODEC_ID_PCM_S32LE, 0x40004 },
+    { CODEC_ID_PCM_F32LE, 0x00004 },
+    { CODEC_ID_PCM_F64LE, 0x00008 },
+    { CODEC_ID_NONE,      0       },
+};
+
+static const AVCodecTag be_codec_tags[] = {
+    { CODEC_ID_PCM_S16BE, 0x00002 },
+    { CODEC_ID_PCM_S24BE, 0x00003 },
+    { CODEC_ID_PCM_S32BE, 0x40004 },
+    { CODEC_ID_PCM_F32BE, 0x00004 },
+    { CODEC_ID_PCM_F64BE, 0x00008 },
+    { CODEC_ID_NONE,      0       },
+};
+
+static const uint32_t be_tags[] = {
+    0x64A30200, // Sun (native)
+    0x64A30400, // NeXT
+    0x0001A364, // VAX
+    0x0003A364, // MIPS (SGI)
+};
+
+static const uint32_t le_tags[] = {
+    0x64A30100, // VAX (native)
+    0x64A30300, // MIPS (DECstation)
+    0x0002A364, // Sun
+};
+
+static int identify(uint32_t tag)
+{
+    int i;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(be_tags); i++)
+        if (be_tags[i] == tag)
+            return 1;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(le_tags); i++)
+        if (le_tags[i] == tag)
+            return 0;
+
+    return -1;
+}
+
+static enum CodecID get_codec(uint32_t tag, int be)
+{
+    const AVCodecTag *tags = be ? be_codec_tags : le_codec_tags;
+    enum CodecID codec_id = ff_codec_get_id(tags, tag);
+    if (codec_id == CODEC_ID_NONE)
+        codec_id = ff_codec_get_id(generic_codec_tags, tag);
+    return codec_id;
+}
+
+static int probe(AVProbeData *p)
+{
+    unsigned char *buf = p->buf;
+    int be, channels, sample_rate;
+    uint32_t tag;
+
+    if (p->buf_size <= 16)
+        return 0;
+
+    be = identify(AV_RB32(buf));
+    if (be < 0)
+        return 0;
+
+    if (be) {
+        sample_rate = av_int2flt(AV_RB32(buf + 4));
+        channels    = AV_RB32(buf + 8);
+        tag         = AV_RB32(buf + 12);
+    } else {
+        sample_rate = av_int2flt(AV_RL32(buf + 4));
+        channels    = AV_RL32(buf + 8);
+        tag         = AV_RL32(buf + 12);
+    }
+
+    if (sample_rate <= 0 || channels <= 0 || channels > 256)
+        return 0;
+
+    tag = be ? AV_RB32(buf + 12) : AV_RL32(buf + 12);
+    if (get_codec(tag, be) == CODEC_ID_NONE)
+        return 0;
+
+    return AVPROBE_SCORE_MAX;
+}
+
+static int read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+    ByteIOContext *pb = s->pb;
+    AVStream *st;
+    AVCodecContext *dec;
+    uint32_t tag;
+    int be, bits_per_frame, chunk, sz;
+    char *comment;
+
+    be = identify(get_be32(pb));
+    if (be < 0)
+        return AVERROR_INVALIDDATA;
+
+    st = av_new_stream(s, 0);
+    if (!st)
+        return AVERROR(ENOMEM);
+
+    dec = st->codec;
+    dec->codec_type = AVMEDIA_TYPE_AUDIO;
+
+    if (be) {
+        dec->sample_rate = av_int2flt(get_be32(pb));
+        dec->channels    = get_be32(pb);
+        tag              = get_be32(pb);
+    } else {
+        dec->sample_rate = av_int2flt(get_le32(pb));
+        dec->channels    = get_le32(pb);
+        tag              = get_le32(pb);
+    }
+
+    if (dec->sample_rate <= 0) {
+        av_log(s, AV_LOG_ERROR, "invalid sample rate %d\n", dec->sample_rate);
+        return AVERROR_INVALIDDATA;
+    }
+
+    dec->codec_id = get_codec(tag, be);
+
+    av_set_pts_info(st, 64, 1, dec->sample_rate);
+
+    while (url_ftell(pb) < 1024) {
+        if (be) {
+            chunk = get_be16(pb);
+            sz    = get_be16(pb);
+        } else {
+            chunk = get_le16(pb);
+            sz    = get_le16(pb);
+        }
+
+        if (!chunk) // end chunk
+            break;
+
+        switch (chunk) {
+        case 2: // comment
+            comment = av_malloc(sz + 1);
+            if (get_buffer(pb, comment, sz) != sz) {
+                av_freep(&comment);
+                return AVERROR(EIO);
+            }
+            comment[sz] = 0;
+            av_metadata_set2(&s->metadata, "comment", comment,
+                             AV_METADATA_DONT_STRDUP_VAL);
+            break;
+        default:
+            url_fskip(pb, sz);
+            break;
+        }
+    }
+
+    url_fseek(pb, 1024, SEEK_SET);
+
+    dec->bits_per_coded_sample = av_get_bits_per_sample(dec->codec_id);
+    bits_per_frame   = dec->channels * dec->bits_per_coded_sample;
+    dec->block_align = bits_per_frame >> 3;
+    dec->bit_rate    = bits_per_frame * dec->sample_rate;
+
+    return 0;
+}
+
+#define NUM_SAMPLES 1024
+
+static int read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    return av_get_packet(s->pb, pkt,
+                         NUM_SAMPLES * s->streams[0]->codec->block_align);
+}
+
+AVInputFormat ircamsf_demuxer = {
+    "ircamsf",
+    NULL_IF_CONFIG_SMALL("IRCAM Sound Format"),
+    0,
+    probe,
+    read_header,
+    read_packet,
+    NULL,
+    pcm_read_seek,
+    .flags = AVFMT_GENERIC_INDEX,
+};
-- 
1.7.0.2



More information about the ffmpeg-devel mailing list