4xm.c 11.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 * 4X Technologies .4xm File Demuxer (no muxer)
 * Copyright (c) 2003  The ffmpeg Project
 *
 * This library 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 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/**
 * @file 4xm.c
 * 4X Technologies file demuxer
 * by Mike Melanson (melanson@pcisys.net)
 * for more information on the .4xm file format, visit:
 *   http://www.pcisys.net/~melanson/codecs/
 */

#include "avformat.h"

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#define  RIFF_TAG MKTAG('R', 'I', 'F', 'F')
#define _4XMV_TAG MKTAG('4', 'X', 'M', 'V')
#define  LIST_TAG MKTAG('L', 'I', 'S', 'T')
#define  HEAD_TAG MKTAG('H', 'E', 'A', 'D')
#define  TRK__TAG MKTAG('T', 'R', 'K', '_')
#define  MOVI_TAG MKTAG('M', 'O', 'V', 'I')
#define  VTRK_TAG MKTAG('V', 'T', 'R', 'K')
#define  STRK_TAG MKTAG('S', 'T', 'R', 'K')
#define  std__TAG MKTAG('s', 't', 'd', '_')
#define  name_TAG MKTAG('n', 'a', 'm', 'e')
#define  vtrk_TAG MKTAG('v', 't', 'r', 'k')
#define  strk_TAG MKTAG('s', 't', 'r', 'k')
#define  ifrm_TAG MKTAG('i', 'f', 'r', 'm')
#define  pfrm_TAG MKTAG('p', 'f', 'r', 'm')
#define  cfrm_TAG MKTAG('c', 'f', 'r', 'm')
#define  snd__TAG MKTAG('s', 'n', 'd', '_')
46 47 48 49 50

#define vtrk_SIZE 0x44
#define strk_SIZE 0x28

#define GET_LIST_HEADER() \
51
    fourcc_tag = get_le32(pb); \
52 53 54
    size = get_le32(pb); \
    if (fourcc_tag != LIST_TAG) \
        return AVERROR_INVALIDDATA; \
55
    fourcc_tag = get_le32(pb);
56 57 58 59 60

typedef struct AudioTrack {
    int sample_rate;
    int bits;
    int channels;
61
    int stream_index;
Michael Niedermayer's avatar
Michael Niedermayer committed
62
    int adpcm;
63 64 65 66 67
} AudioTrack;

typedef struct FourxmDemuxContext {
    int width;
    int height;
68
    int video_stream_index;
69 70 71
    int track_count;
    AudioTrack *tracks;
    int selected_track;
72

73 74 75
    int64_t audio_pts;
    int64_t video_pts;
    int video_pts_inc;
76
    float fps;
77 78
} FourxmDemuxContext;

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
static float get_le_float(unsigned char *buffer)
{
    float f;
    unsigned char *float_buffer = (unsigned char *)&f;

#ifdef WORDS_BIGENDIAN
    float_buffer[0] = buffer[3];
    float_buffer[1] = buffer[2];
    float_buffer[2] = buffer[1];
    float_buffer[3] = buffer[0];
#else
    float_buffer[0] = buffer[0];
    float_buffer[1] = buffer[1];
    float_buffer[2] = buffer[2];
    float_buffer[3] = buffer[3];
#endif

    return f;
}

99 100
static int fourxm_probe(AVProbeData *p)
{
101 102 103
    if (p->buf_size < 12)
        return 0;

104 105
    if ((LE_32(&p->buf[0]) != RIFF_TAG) ||
        (LE_32(&p->buf[8]) != _4XMV_TAG))
106 107 108 109 110 111
        return 0;

    return AVPROBE_SCORE_MAX;
}

static int fourxm_read_header(AVFormatContext *s,
112
                              AVFormatParameters *ap)
113 114 115 116 117 118 119 120 121 122 123 124 125 126
{
    ByteIOContext *pb = &s->pb;
    unsigned int fourcc_tag;
    unsigned int size;
    int header_size;
    FourxmDemuxContext *fourxm = (FourxmDemuxContext *)s->priv_data;
    unsigned char *header;
    int i;
    int current_track = -1;
    AVStream *st;

    fourxm->track_count = 0;
    fourxm->tracks = NULL;
    fourxm->selected_track = 0;
127
    fourxm->fps = 1.0;
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

    /* skip the first 3 32-bit numbers */
    url_fseek(pb, 12, SEEK_CUR);

    /* check for LIST-HEAD */
    GET_LIST_HEADER();
    header_size = size - 4;
    if (fourcc_tag != HEAD_TAG)
        return AVERROR_INVALIDDATA;

    /* allocate space for the header and load the whole thing */
    header = av_malloc(header_size);
    if (!header)
        return AVERROR_NOMEM;
    if (get_buffer(pb, header, header_size) != header_size)
        return AVERROR_IO;

    /* take the lazy approach and search for any and all vtrk and strk chunks */
    for (i = 0; i < header_size - 8; i++) {
147
        fourcc_tag = LE_32(&header[i]);
148 149
        size = LE_32(&header[i + 4]);

150
        if (fourcc_tag == std__TAG) {
151 152
            fourxm->fps = get_le_float(&header[i + 12]);
            fourxm->video_pts_inc = (int)(90000.0 / fourxm->fps);
153
        } else if (fourcc_tag == vtrk_TAG) {
154 155 156 157 158 159 160 161
            /* check that there is enough data */
            if (size != vtrk_SIZE) {
                av_free(header);
                return AVERROR_INVALIDDATA;
            }
            fourxm->width = LE_32(&header[i + 36]);
            fourxm->height = LE_32(&header[i + 40]);
            i += 8 + size;
162 163 164 165 166

            /* allocate a new AVStream */
            st = av_new_stream(s, 0);
            if (!st)
                return AVERROR_NOMEM;
167
            av_set_pts_info(st, 33, 1, 90000);
168 169 170

            fourxm->video_stream_index = st->index;

171 172
            st->codec.frame_rate = fourxm->fps;
            st->codec.frame_rate_base = 1.0;
173 174 175 176 177 178
            st->codec.codec_type = CODEC_TYPE_VIDEO;
            st->codec.codec_id = CODEC_ID_4XM;
            st->codec.codec_tag = 0;  /* no fourcc */
            st->codec.width = fourxm->width;
            st->codec.height = fourxm->height;

179 180 181 182 183 184 185 186
        } else if (fourcc_tag == strk_TAG) {
            /* check that there is enough data */
            if (size != strk_SIZE) {
                av_free(header);
                return AVERROR_INVALIDDATA;
            }
            current_track = LE_32(&header[i + 8]);
            if (current_track + 1 > fourxm->track_count) {
Mike Melanson's avatar
Mike Melanson committed
187 188 189
                fourxm->track_count = current_track + 1;
                fourxm->tracks = av_realloc(fourxm->tracks, 
                    fourxm->track_count * sizeof(AudioTrack));
190 191 192 193 194
                if (!fourxm->tracks) {
                    av_free(header);
                    return AVERROR_NOMEM;
                }
            }
Michael Niedermayer's avatar
Michael Niedermayer committed
195
            fourxm->tracks[current_track].adpcm = LE_32(&header[i + 12]);
196 197 198 199
            fourxm->tracks[current_track].channels = LE_32(&header[i + 36]);
            fourxm->tracks[current_track].sample_rate = LE_32(&header[i + 40]);
            fourxm->tracks[current_track].bits = LE_32(&header[i + 44]);
            i += 8 + size;
200 201 202 203 204 205

            /* allocate a new AVStream */
            st = av_new_stream(s, current_track);
            if (!st)
                return AVERROR_NOMEM;

206 207 208
            /* set the pts reference (1 pts = 1/90000) */
            av_set_pts_info(st, 33, 1, 90000);

209 210 211 212 213 214 215 216 217 218
            fourxm->tracks[current_track].stream_index = st->index;

            st->codec.codec_type = CODEC_TYPE_AUDIO;
            st->codec.codec_tag = 1;
            st->codec.channels = fourxm->tracks[current_track].channels;
            st->codec.sample_rate = fourxm->tracks[current_track].sample_rate;
            st->codec.bits_per_sample = fourxm->tracks[current_track].bits;
            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;
Michael Niedermayer's avatar
Michael Niedermayer committed
219 220 221
            if (fourxm->tracks[current_track].adpcm)
                st->codec.codec_id = CODEC_ID_ADPCM_4XM;
            else if (st->codec.bits_per_sample == 8)
222 223 224
                st->codec.codec_id = CODEC_ID_PCM_U8;
            else
                st->codec.codec_id = CODEC_ID_PCM_S16LE;
225 226 227 228 229 230 231 232 233 234
        }
    }

    av_free(header);

    /* skip over the LIST-MOVI chunk (which is where the stream should be */
    GET_LIST_HEADER();
    if (fourcc_tag != MOVI_TAG)
        return AVERROR_INVALIDDATA;

235
    /* initialize context members */
236 237
    fourxm->video_pts = -fourxm->video_pts_inc;  /* first frame will push to 0 */
    fourxm->audio_pts = 0;
238

239 240 241 242
    return 0;
}

static int fourxm_read_packet(AVFormatContext *s,
243
                              AVPacket *pkt)
244 245 246 247
{
    FourxmDemuxContext *fourxm = s->priv_data;
    ByteIOContext *pb = &s->pb;
    unsigned int fourcc_tag;
Michael Niedermayer's avatar
Michael Niedermayer committed
248
    unsigned int size, out_size;
249 250 251
    int ret = 0;
    int track_number;
    int packet_read = 0;
252 253
    unsigned char header[8];
    int64_t pts_inc;
254
    int audio_frame_count;
255 256 257

    while (!packet_read) {

258 259 260 261
        if ((ret = get_buffer(&s->pb, header, 8)) < 0)
            return ret;
        fourcc_tag = LE_32(&header[0]);
        size = LE_32(&header[4]);
262
        if (url_feof(pb))
263
            return AVERROR_IO;
264 265
        switch (fourcc_tag) {

266
        case LIST_TAG:
267 268 269
            /* this is a good time to bump the video pts */
            fourxm->video_pts += fourxm->video_pts_inc;

270 271 272 273
            /* skip the LIST-* tag and move on to the next fourcc */
            get_le32(pb);
            break;

274 275
        case ifrm_TAG:
        case pfrm_TAG:
276
        case cfrm_TAG:{
277 278 279 280

            /* allocate 8 more bytes than 'size' to account for fourcc
             * and size */
            if (av_new_packet(pkt, size + 8))
281
                return AVERROR_IO;
282
            pkt->stream_index = fourxm->video_stream_index;
283
            pkt->pts = fourxm->video_pts;
284 285 286 287 288 289 290
            memcpy(pkt->data, header, 8);
            ret = get_buffer(&s->pb, &pkt->data[8], size);

            if (ret < 0)
                av_free_packet(pkt);
            else
                packet_read = 1;
291 292
            break;
        }
293

294 295
        case snd__TAG:
            track_number = get_le32(pb);
Michael Niedermayer's avatar
Michael Niedermayer committed
296 297 298
            out_size= get_le32(pb);
            size-=8;

299 300
            if (track_number == fourxm->selected_track) {
                if (av_new_packet(pkt, size))
301
                    return AVERROR_IO;
302 303
                pkt->stream_index = 
                    fourxm->tracks[fourxm->selected_track].stream_index;
304
                pkt->pts = fourxm->audio_pts;
305 306 307
                ret = get_buffer(&s->pb, pkt->data, size);
                if (ret < 0)
                    av_free_packet(pkt);
308 309 310
                else
                    packet_read = 1;

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
                /* pts accounting */
                audio_frame_count = size;
                if (fourxm->tracks[fourxm->selected_track].adpcm)
                    audio_frame_count -= 
                        2 * (fourxm->tracks[fourxm->selected_track].channels);
                audio_frame_count /=
                      fourxm->tracks[fourxm->selected_track].channels;
                if (fourxm->tracks[fourxm->selected_track].adpcm)
                    audio_frame_count *= 2;
                else 
                    audio_frame_count /=
                    (fourxm->tracks[fourxm->selected_track].bits / 8);
                pts_inc = audio_frame_count;
                pts_inc *= 90000;
                pts_inc /= fourxm->tracks[fourxm->selected_track].sample_rate;
                fourxm->audio_pts += pts_inc;
327

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
            } else {
                url_fseek(pb, size, SEEK_CUR);
            }
            break;

        default:
            url_fseek(pb, size, SEEK_CUR);
            break;
        }
    }
    return ret;
}

static int fourxm_read_close(AVFormatContext *s)
{
    FourxmDemuxContext *fourxm = (FourxmDemuxContext *)s->priv_data;

    av_free(fourxm->tracks);

    return 0;
}

static AVInputFormat fourxm_iformat = {
    "4xm",
    "4X Technologies format",
    sizeof(FourxmDemuxContext),
    fourxm_probe,
    fourxm_read_header,
    fourxm_read_packet,
    fourxm_read_close,
};

int fourxm_init(void)
{
    av_register_input_format(&fourxm_iformat);
    return 0;
}