idroq.c 8.65 KB
Newer Older
1 2 3 4
/*
 * Id RoQ (.roq) File Demuxer
 * Copyright (c) 2003 The ffmpeg Project
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * 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 14 15 16 17
 * 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
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 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
 */

/**
 * @file idroq.c
 * Id RoQ format file demuxer
 * by Mike Melanson (melanson@pcisys.net)
 * for more information on the .roq file format, visit:
 *   http://www.csse.monash.edu.au/~timf/
 */

#include "avformat.h"

#define RoQ_MAGIC_NUMBER 0x1084
#define RoQ_CHUNK_PREAMBLE_SIZE 8
#define RoQ_AUDIO_SAMPLE_RATE 22050
#define RoQ_CHUNKS_TO_SCAN 30

#define RoQ_INFO           0x1001
#define RoQ_QUAD_CODEBOOK  0x1002
#define RoQ_QUAD_VQ        0x1011
#define RoQ_SOUND_MONO     0x1020
#define RoQ_SOUND_STEREO   0x1021

typedef struct RoqDemuxContext {

    int width;
    int height;
    int audio_channels;
    int framerate;
    int frame_pts_inc;

    int video_stream_index;
    int audio_stream_index;

    int64_t video_pts;
    unsigned int audio_frame_count;

} RoqDemuxContext;

static int roq_probe(AVProbeData *p)
{
61 62
    if ((AV_RL16(&p->buf[0]) != RoQ_MAGIC_NUMBER) ||
        (AV_RL32(&p->buf[2]) != 0xFFFFFFFF))
63 64 65 66 67 68 69 70 71
        return 0;

    return AVPROBE_SCORE_MAX;
}

static int roq_read_header(AVFormatContext *s,
                           AVFormatParameters *ap)
{
    RoqDemuxContext *roq = s->priv_data;
72
    ByteIOContext *pb = s->pb;
73 74 75 76 77 78 79
    AVStream *st;
    unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE];
    int i;
    unsigned int chunk_size;
    unsigned int chunk_type;

    /* get the main header */
80
    if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) !=
81
        RoQ_CHUNK_PREAMBLE_SIZE)
82
        return AVERROR(EIO);
83
    roq->framerate = AV_RL16(&preamble[6]);
84 85 86
    roq->frame_pts_inc = 90000 / roq->framerate;

    /* init private context parameters */
87
    roq->width = roq->height = roq->audio_channels = roq->video_pts =
88 89 90 91
    roq->audio_frame_count = 0;

    /* scan the first n chunks searching for A/V parameters */
    for (i = 0; i < RoQ_CHUNKS_TO_SCAN; i++) {
92
        if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) !=
93
            RoQ_CHUNK_PREAMBLE_SIZE)
94
            return AVERROR(EIO);
95

96 97
        chunk_type = AV_RL16(&preamble[0]);
        chunk_size = AV_RL32(&preamble[2]);
98 99 100 101 102

        switch (chunk_type) {

        case RoQ_INFO:
            /* fetch the width and height; reuse the preamble bytes */
103
            if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) !=
104
                RoQ_CHUNK_PREAMBLE_SIZE)
105
                return AVERROR(EIO);
106 107
            roq->width = AV_RL16(&preamble[0]);
            roq->height = AV_RL16(&preamble[2]);
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
            break;

        case RoQ_QUAD_CODEBOOK:
        case RoQ_QUAD_VQ:
            /* ignore during this scan */
            url_fseek(pb, chunk_size, SEEK_CUR);
            break;

        case RoQ_SOUND_MONO:
            roq->audio_channels = 1;
            url_fseek(pb, chunk_size, SEEK_CUR);
            break;

        case RoQ_SOUND_STEREO:
            roq->audio_channels = 2;
            url_fseek(pb, chunk_size, SEEK_CUR);
            break;

        default:
127
            av_log(s, AV_LOG_ERROR, " unknown RoQ chunk type (%04X)\n", AV_RL16(&preamble[0]));
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
            return AVERROR_INVALIDDATA;
            break;
        }

        /* if all necessary parameters have been gathered, exit early */
        if ((roq->width && roq->height) && roq->audio_channels)
            break;
    }

    /* seek back to the first chunk */
    url_fseek(pb, RoQ_CHUNK_PREAMBLE_SIZE, SEEK_SET);

    /* initialize the decoders */
    st = av_new_stream(s, 0);
    if (!st)
143
        return AVERROR(ENOMEM);
144 145
    /* set the pts reference (1 pts = 1/90000) */
    av_set_pts_info(st, 33, 1, 90000);
146
    roq->video_stream_index = st->index;
147 148 149 150 151
    st->codec->codec_type = CODEC_TYPE_VIDEO;
    st->codec->codec_id = CODEC_ID_ROQ;
    st->codec->codec_tag = 0;  /* no fourcc */
    st->codec->width = roq->width;
    st->codec->height = roq->height;
152 153 154 155

    if (roq->audio_channels) {
        st = av_new_stream(s, 0);
        if (!st)
156
            return AVERROR(ENOMEM);
157
        av_set_pts_info(st, 33, 1, 90000);
158
        roq->audio_stream_index = st->index;
159 160 161 162 163 164 165 166 167
        st->codec->codec_type = CODEC_TYPE_AUDIO;
        st->codec->codec_id = CODEC_ID_ROQ_DPCM;
        st->codec->codec_tag = 0;  /* no tag */
        st->codec->channels = roq->audio_channels;
        st->codec->sample_rate = RoQ_AUDIO_SAMPLE_RATE;
        st->codec->bits_per_sample = 16;
        st->codec->bit_rate = st->codec->channels * st->codec->sample_rate *
            st->codec->bits_per_sample;
        st->codec->block_align = st->codec->channels * st->codec->bits_per_sample;
168 169 170 171 172 173 174 175 176
    }

    return 0;
}

static int roq_read_packet(AVFormatContext *s,
                           AVPacket *pkt)
{
    RoqDemuxContext *roq = s->priv_data;
177
    ByteIOContext *pb = s->pb;
178 179 180 181 182 183 184 185 186 187
    int ret = 0;
    unsigned int chunk_size;
    unsigned int chunk_type;
    unsigned int codebook_size;
    unsigned char preamble[RoQ_CHUNK_PREAMBLE_SIZE];
    int packet_read = 0;
    offset_t codebook_offset;

    while (!packet_read) {

188
        if (url_feof(s->pb))
189
            return AVERROR(EIO);
190 191

        /* get the next chunk preamble */
192
        if ((ret = get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE)) !=
193
            RoQ_CHUNK_PREAMBLE_SIZE)
194
            return AVERROR(EIO);
195

196 197
        chunk_type = AV_RL16(&preamble[0]);
        chunk_size = AV_RL32(&preamble[2]);
198 199
        if(chunk_size > INT_MAX)
            return AVERROR_INVALIDDATA;
200 201 202 203 204 205 206 207 208 209 210 211 212

        switch (chunk_type) {

        case RoQ_INFO:
            /* don't care about this chunk anymore */
            url_fseek(pb, RoQ_CHUNK_PREAMBLE_SIZE, SEEK_CUR);
            break;

        case RoQ_QUAD_CODEBOOK:
            /* packet needs to contain both this codebook and next VQ chunk */
            codebook_offset = url_ftell(pb) - RoQ_CHUNK_PREAMBLE_SIZE;
            codebook_size = chunk_size;
            url_fseek(pb, codebook_size, SEEK_CUR);
213
            if (get_buffer(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE) !=
214
                RoQ_CHUNK_PREAMBLE_SIZE)
215
                return AVERROR(EIO);
216
            chunk_size = AV_RL32(&preamble[2]) + RoQ_CHUNK_PREAMBLE_SIZE * 2 +
217 218 219 220 221 222
                codebook_size;

            /* rewind */
            url_fseek(pb, codebook_offset, SEEK_SET);

            /* load up the packet */
Michael Niedermayer's avatar
Michael Niedermayer committed
223 224
            ret= av_get_packet(pb, pkt, chunk_size);
            if (ret != chunk_size)
225
                return AVERROR(EIO);
226 227 228 229 230 231 232 233 234 235 236 237
            pkt->stream_index = roq->video_stream_index;
            pkt->pts = roq->video_pts;

            roq->video_pts += roq->frame_pts_inc;
            packet_read = 1;
            break;

        case RoQ_SOUND_MONO:
        case RoQ_SOUND_STEREO:
        case RoQ_QUAD_VQ:
            /* load up the packet */
            if (av_new_packet(pkt, chunk_size + RoQ_CHUNK_PREAMBLE_SIZE))
238
                return AVERROR(EIO);
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
            /* copy over preamble */
            memcpy(pkt->data, preamble, RoQ_CHUNK_PREAMBLE_SIZE);

            if (chunk_type == RoQ_QUAD_VQ) {
                pkt->stream_index = roq->video_stream_index;
                pkt->pts = roq->video_pts;
                roq->video_pts += roq->frame_pts_inc;
            } else {
                pkt->stream_index = roq->audio_stream_index;
                pkt->pts = roq->audio_frame_count;
                pkt->pts *= 90000;
                pkt->pts /= RoQ_AUDIO_SAMPLE_RATE;
                roq->audio_frame_count += (chunk_size / roq->audio_channels);
            }

Michael Niedermayer's avatar
Michael Niedermayer committed
254
            pkt->pos= url_ftell(pb);
255 256
            ret = get_buffer(pb, pkt->data + RoQ_CHUNK_PREAMBLE_SIZE,
                chunk_size);
257
            if (ret != chunk_size)
258
                ret = AVERROR(EIO);
259 260 261 262 263

            packet_read = 1;
            break;

        default:
264
            av_log(s, AV_LOG_ERROR, "  unknown RoQ chunk (%04X)\n", chunk_type);
265 266 267 268 269 270 271 272 273 274
            return AVERROR_INVALIDDATA;
            break;
        }
    }

    return ret;
}

static int roq_read_close(AVFormatContext *s)
{
275
//    RoqDemuxContext *roq = s->priv_data;
276 277 278 279

    return 0;
}

280
AVInputFormat roq_demuxer = {
281 282 283 284 285 286 287 288
    "RoQ",
    "Id RoQ format",
    sizeof(RoqDemuxContext),
    roq_probe,
    roq_read_header,
    roq_read_packet,
    roq_read_close,
};