[FFmpeg-devel] [PATCH] WIP Vivo demuxer

Daniel Verkamp daniel
Sat Mar 21 16:32:52 CET 2009


Hi,

Starting a new thread to avoid cluttering the one about qualification tasks...

NB: This patch is still a huge mess and not intended for review per se yet.

I cleaned up the demuxer a little more and tested with some more
samples; it does in fact produce working H.263 for some samples, but
for others it seems to always fail starting with [h263 @ ...]Bad UFEP
type (2) followed by a lot of Bad UFEP type (6).

Example of working sample: http://samples.mplayerhq.hu/vivo/gegebee.viv
Example of broken sample: http://samples.mplayerhq.hu/vivo/take10.viv

Now, there is something broken with timestamps, I think; when I try to
copy the frames with -f image2, no output is produced, and ffmpeg
prints something like "video:0kB audio:0kB global headers:0kB muxing
overhead -inf%".  Also playing the working samples with ffplay results
in much faster playback than is correct.  This is the point at which I
throw up my hands and say that I don't know how this PTS stuff works
and hope that someone will take a look and tell me what I'm doing
wrong. :)

Thanks,
-- Daniel Verkamp
-------------- next part --------------
>From 652f4b9363c087a2f3377f2dd40311feebc19b61 Mon Sep 17 00:00:00 2001
From: Daniel Verkamp <daniel at drv.nu>
Date: Fri, 20 Mar 2009 20:21:49 -0500
Subject: [PATCH] work in progress vivo demuxer

---
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    1 +
 libavformat/vivodec.c    |  317 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 319 insertions(+), 0 deletions(-)
 create mode 100644 libavformat/vivodec.c

diff --git a/libavformat/Makefile b/libavformat/Makefile
index f2285d7..5f64edd 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -214,6 +214,7 @@ OBJS-$(CONFIG_TXD_DEMUXER)               += txd.o
 OBJS-$(CONFIG_VC1_DEMUXER)               += raw.o
 OBJS-$(CONFIG_VC1T_DEMUXER)              += vc1test.o
 OBJS-$(CONFIG_VC1T_MUXER)                += vc1testenc.o
+OBJS-$(CONFIG_VIVO_DEMUXER)              += vivodec.o
 OBJS-$(CONFIG_VMD_DEMUXER)               += sierravmd.o
 OBJS-$(CONFIG_VOC_DEMUXER)               += vocdec.o voc.o
 OBJS-$(CONFIG_VOC_MUXER)                 += vocenc.o voc.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 39ac3b8..2e7593e 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -187,6 +187,7 @@ void av_register_all(void)
     REGISTER_DEMUXER  (TXD, txd);
     REGISTER_DEMUXER  (VC1, vc1);
     REGISTER_MUXDEMUX (VC1T, vc1t);
+    REGISTER_DEMUXER  (VIVO, vivo);
     REGISTER_DEMUXER  (VMD, vmd);
     REGISTER_MUXDEMUX (VOC, voc);
     REGISTER_DEMUXER  (VQF, vqf);
diff --git a/libavformat/vivodec.c b/libavformat/vivodec.c
new file mode 100644
index 0000000..73cb454
--- /dev/null
+++ b/libavformat/vivodec.c
@@ -0,0 +1,317 @@
+/*
+ * Vivo stream demuxer
+ * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
+ *
+ * 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
+ */
+
+/**
+ * @file libavformat/vivodec.c
+ * @brief Vivo stream demuxer
+ * @author Daniel Verkamp <daniel at drv.nu>
+ * @sa http://wiki.multimedia.cx/index.php?title=Vivo
+ */
+
+#include "libavutil/intreadwrite.h"
+#include "avformat.h"
+
+typedef struct VivoPacket {
+    unsigned type;
+    unsigned sequence;
+    unsigned len;
+} VivoPacket;
+
+typedef struct VivoContext {
+    VivoPacket pkt;
+    AVRational timebase;
+    int video_pts;
+    int audio_pts;
+} VivoContext;
+
+
+static int vivo_probe(AVProbeData *p)
+{
+    const unsigned char *buf = p->buf;
+    unsigned c, len = 0, i;
+
+    c = *buf++;
+
+    // stream must start with packet of type 0 and sequence number 0
+    if (c != 0)
+        return 0;
+
+    // read at most 2 bytes of coded length
+    for (i = 0; i < 2; i++) {
+        c = *buf++;
+        len = (len << 7) | (c & 0x7F);
+        if (~c & 0x80)
+            break;
+    }
+
+    if (i == 2 || len > 1024 || len < 21)
+        return 0;
+
+    if (memcmp(buf, "\r\nVersion:Vivo/", 15))
+        return 0;
+    buf += 15;
+
+    if (*buf != '0' && *buf != '1' && *buf != '2')
+        return 0;
+
+    return AVPROBE_SCORE_MAX;
+}
+
+
+static int vivo_get_packet_header(AVFormatContext *s, VivoPacket *vpkt)
+{
+    ByteIOContext *pb = s->pb;
+    unsigned c, get_len = 0;
+
+    // TODO: check for eof
+
+    c = get_byte(pb);
+
+    if (c == 0x82) {
+        get_len = 1;
+        c = get_byte(pb);
+    }
+
+    vpkt->type     = c >> 4;
+    vpkt->sequence = c & 0xF;
+
+    switch (vpkt->type) {
+    case 0:   get_len =   1; break;
+    case 1: vpkt->len = 128; break;
+    case 2:   get_len =   1; break;
+    case 3: vpkt->len =  40; break;
+    case 4: vpkt->len =  24; break;
+    default:
+        av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vpkt->type);
+        return -1;
+    }
+
+    if (get_len) {
+        vpkt->len = 0;
+        do {
+            c = get_byte(pb);
+            vpkt->len = (vpkt->len << 7) | (c & 0x7F);
+            // FIXME - check for EOF?
+        } while (c & 0x80);
+    }
+
+    return 0;
+}
+
+
+static int vivo_read_text_header(AVFormatContext *s, VivoPacket *vpkt,
+                                 AVStream *vst, AVStream *ast)
+{
+    VivoContext *vivo = s->priv_data;
+    ByteIOContext *pb = s->pb;
+    unsigned char text[vpkt->len + 1];
+    unsigned char *line, *line_end, *key, *value;
+    int value_int;
+
+    get_buffer(pb, text, vpkt->len);
+    text[vpkt->len] = '\0';
+
+    line = text;
+    while (*line) {
+        line_end = strstr(line, "\r\n");
+        if (!line_end)
+            break;
+
+        *line_end = '\0';
+        key = line;
+        line = line_end + 2; // skip \r\n
+
+        if (line_end == key) // skip blank lines
+            continue;
+
+        value = strchr(key, ':');
+        if (!value) {
+            av_log(s, AV_LOG_ERROR, "missing colon in key:value pair\n");
+            return -1;
+        }
+
+        *value++ = '\0';
+        value_int = atoi(value);
+
+        av_log(s, AV_LOG_INFO, "header: '%s' = '%s'\n", key, value);
+
+        if (!strcmp(key, "Width")) {
+            vst->codec->width = value_int;
+        } else if (!strcmp(key, "Height")) {
+            vst->codec->height = value_int;
+        } else if (!strcmp(key, "TimeUnitNumerator")) {
+            vivo->timebase.num = value_int;
+        } else if (!strcmp(key, "TimeUnitDenominator")) {
+            vivo->timebase.den = value_int;
+        } else if (!strcmp(key, "SamplingFrequency")) {
+            ast->codec->sample_rate = value_int;
+            if (value_int == 16000) {
+                //ast->codec->codec_id = CODEC_ID_SIREN;
+            } else if (value_int == 8000) {
+                //ast->codec->codec_id = CODEC_ID_G723;
+            }
+        } else {
+            av_metadata_set(&s->metadata, key, value); // TODO - FIXME?
+        }
+    }
+
+    return 0;
+}
+
+
+static int vivo_read_header(AVFormatContext *s, AVFormatParameters *ap)
+{
+    VivoContext *vivo = s->priv_data;
+    AVStream *vst;
+    AVStream *ast;
+    int ret;
+
+    vst = av_new_stream(s, 0);
+    if (!vst)
+        return AVERROR(ENOMEM);
+    vst->codec->codec_type = CODEC_TYPE_VIDEO;
+    vst->codec->codec_id   = CODEC_ID_H263;
+
+    ast = av_new_stream(s, 1);
+    if (!ast)
+        return AVERROR(ENOMEM); // FIXME - memleak etc.
+    ast->codec->codec_type = CODEC_TYPE_AUDIO;
+    ast->codec->channels = 1;
+    ast->codec->bits_per_coded_sample = 8;
+    ast->codec->sample_fmt = SAMPLE_FMT_U8;
+
+    // FIXME: hack
+    //ast->codec->codec_id = CODEC_ID_PCM_U8;
+
+    while (1) {
+        ret = vivo_get_packet_header(s, &vivo->pkt);
+        if (ret < 0)
+            return -1; // FIXME - memleak etc.
+
+        // done reading all text header packets?
+        if (vivo->pkt.sequence || vivo->pkt.type)
+            break;
+
+        ret = vivo_read_text_header(s, &vivo->pkt, vst, ast);
+        if (ret < 0)
+            return -1; // FIXME - memleak etc.
+    }
+
+    av_set_pts_info(vst, 64, vivo->timebase.num, vivo->timebase.den);
+    av_set_pts_info(ast, 64, 1, ast->codec->sample_rate);
+
+    return 0;
+}
+
+
+static int vivo_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    VivoContext *vivo = s->priv_data;
+    ByteIOContext *pb = s->pb;
+    int ret, stream_index;
+    unsigned old_sequence = vivo->pkt.sequence, old_type = vivo->pkt.type;
+    unsigned len = 0;
+    uint8_t *buf = NULL, *buf_new;
+
+restart:
+
+    if (url_feof(pb)) {
+        av_log(s, AV_LOG_ERROR, "EOF 1\n");
+        return AVERROR_EOF;
+    }
+
+    switch (vivo->pkt.type) {
+    case 0:
+        url_fskip(pb, vivo->pkt.len);
+        ret = vivo_get_packet_header(s, &vivo->pkt);
+        if (ret < 0) {
+            av_log(s, AV_LOG_ERROR, "couldn't get packet header\n");
+            return AVERROR(EIO);
+        }
+        goto restart;
+    case 1: case 2: // video
+        stream_index = 0;
+        break;
+    case 3: case 4: // audio
+        stream_index = 1;
+        break;
+    default:
+        av_log(s, AV_LOG_ERROR, "unknown packet type %d\n", vivo->pkt.type);
+        return -1;
+    }
+
+    do {
+        // read data into buffer
+        buf_new = av_realloc(buf, len + vivo->pkt.len);
+        if (!buf_new) {
+            av_log(s, AV_LOG_ERROR, "couldn't allocate buffer\n");
+            av_free(buf);
+            return AVERROR(ENOMEM);
+        }
+        buf = buf_new;
+        get_buffer(pb, buf + len, vivo->pkt.len);
+        len += vivo->pkt.len;
+
+        if (url_feof(pb)) {
+            av_log(s, AV_LOG_ERROR, "EOF 2\n");
+            av_free(buf);
+            return AVERROR_EOF;
+        }
+
+        // get next packet header
+        ret = vivo_get_packet_header(s, &vivo->pkt);
+        if (ret < 0) {
+            av_log(s, AV_LOG_ERROR, "couldn't get packet header\n");
+            av_free(buf);
+            return AVERROR(EIO);
+        }
+    } while (vivo->pkt.sequence == old_sequence &&
+           (((vivo->pkt.type - 1) >> 1) == ((old_type - 1) >> 1)));
+
+    //av_hex_dump_log(s, AV_LOG_INFO, buf, len);
+
+    // copy data into packet
+    if (av_new_packet(pkt, len) < 0) {
+        av_log(s, AV_LOG_ERROR, "couldn't create packet\n");
+        av_free(buf);
+        return AVERROR(ENOMEM);
+    }
+
+    pkt->stream_index = stream_index;
+    if (stream_index == 0)
+        pkt->pts = vivo->video_pts++;
+    else
+        pkt->pts = vivo->audio_pts++;
+    //av_log(s, AV_LOG_INFO, "final packet size = %d, stream = %d, pts = %d\n", len, pkt->stream_index, (int)pkt->pts);
+    memcpy(pkt->data, buf, len);
+    av_free(buf);
+
+    return len;
+}
+
+AVInputFormat vivo_demuxer = {
+    "vivo",
+    NULL_IF_CONFIG_SMALL("Vivo stream"),
+    sizeof(VivoContext),
+    vivo_probe,
+    vivo_read_header,
+    vivo_read_packet,
+};
-- 
1.6.2



More information about the ffmpeg-devel mailing list