[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