Statistics
| Branch: | Revision:

ffmpeg / libavformat / sierravmd.c @ a7eb3c8d

History | View | Annotate | Download (8.06 KB)

1
/*
2
 * Sierra VMD Format Demuxer
3
 * Copyright (c) 2004 The ffmpeg Project
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 */
19

    
20
/**
21
 * @file sierravmd.c
22
 * Sierra VMD file demuxer
23
 * by Vladimir "VAG" Gneushev (vagsoft at mail.ru)
24
 */
25

    
26
#include "avformat.h"
27

    
28
#define LE_16(x)  ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0])
29
#define LE_32(x)  ((((uint8_t*)(x))[3] << 24) | \
30
                   (((uint8_t*)(x))[2] << 16) | \
31
                   (((uint8_t*)(x))[1] << 8) | \
32
                    ((uint8_t*)(x))[0])
33

    
34
#define VMD_HEADER_SIZE 0x0330
35
#define BYTES_PER_FRAME_RECORD 16
36

    
37
typedef struct {
38
  int stream_index;
39
  offset_t frame_offset;
40
  unsigned int frame_size;
41
  int64_t pts;
42
  int keyframe;
43
  unsigned char frame_record[BYTES_PER_FRAME_RECORD];
44
} vmd_frame_t;
45

    
46
typedef struct VmdDemuxContext {
47
    int video_stream_index;
48
    int audio_stream_index;
49

    
50
    unsigned int audio_type;
51
    unsigned int audio_samplerate;
52
    unsigned int audio_bits;
53
    unsigned int audio_channels;
54

    
55
    unsigned int frame_count;
56
    vmd_frame_t *frame_table;
57
    unsigned int current_frame;
58

    
59
    unsigned char vmd_header[VMD_HEADER_SIZE];
60
} VmdDemuxContext;
61

    
62
static int vmd_probe(AVProbeData *p)
63
{
64
    if (p->buf_size < 2)
65
        return 0;
66

    
67
    /* check if the first 2 bytes of the file contain the appropriate size
68
     * of a VMD header chunk */
69
    if (LE_16(&p->buf[0]) != VMD_HEADER_SIZE - 2)
70
        return 0;
71

    
72
    /* only return half certainty since this check is a bit sketchy */
73
    return AVPROBE_SCORE_MAX / 2;
74
}
75

    
76
static int vmd_read_header(AVFormatContext *s,
77
                           AVFormatParameters *ap)
78
{
79
    VmdDemuxContext *vmd = (VmdDemuxContext *)s->priv_data;
80
    ByteIOContext *pb = &s->pb;
81
    AVStream *st;
82
    unsigned int toc_offset;
83
    unsigned char *raw_frame_table;
84
    int raw_frame_table_size;
85
    unsigned char *current_frame_record;
86
    offset_t current_offset;
87
    int i;
88
    int sample_rate;
89
    unsigned int total_frames;
90
int video_frame_count = 0;
91

    
92
    /* fetch the main header, including the 2 header length bytes */
93
    url_fseek(pb, 0, SEEK_SET);
94
    if (get_buffer(pb, vmd->vmd_header, VMD_HEADER_SIZE) != VMD_HEADER_SIZE)
95
        return -EIO;
96

    
97
    /* start up the decoders */
98
    st = av_new_stream(s, 0);
99
    if (!st)
100
        return AVERROR_NOMEM;
101
    vmd->video_stream_index = st->index;
102
    st->codec.codec_type = CODEC_TYPE_VIDEO;
103
    st->codec.codec_id = CODEC_ID_VMDVIDEO;
104
    st->codec.codec_tag = 0;  /* no fourcc */
105
    st->codec.width = LE_16(&vmd->vmd_header[12]);
106
    st->codec.height = LE_16(&vmd->vmd_header[14]);
107
    st->codec.extradata_size = VMD_HEADER_SIZE;
108
    st->codec.extradata = av_malloc(VMD_HEADER_SIZE);
109
    memcpy(st->codec.extradata, vmd->vmd_header, VMD_HEADER_SIZE);
110

    
111
    /* if sample rate is 0, assume no audio */
112
    sample_rate = LE_16(&vmd->vmd_header[804]);
113
    if (sample_rate) {
114
        st = av_new_stream(s, 0);
115
        if (!st)
116
            return AVERROR_NOMEM;
117
        vmd->audio_stream_index = st->index;
118
        st->codec.codec_type = CODEC_TYPE_AUDIO;
119
        st->codec.codec_id = CODEC_ID_VMDAUDIO;
120
        st->codec.codec_tag = 0;  /* no codec tag */
121
        st->codec.channels = (vmd->vmd_header[811] & 0x80) ? 2 : 1;
122
        st->codec.sample_rate = sample_rate;
123
        st->codec.bit_rate = st->codec.sample_rate * 
124
            st->codec.bits_per_sample * st->codec.channels;
125
        st->codec.block_align = LE_16(&vmd->vmd_header[806]);
126
        if (st->codec.block_align & 0x8000) {
127
            st->codec.bits_per_sample = 16;
128
            st->codec.block_align = -(st->codec.block_align - 0x10000);
129
        } else
130
            st->codec.bits_per_sample = 8;
131
    }
132

    
133
    /* skip over the offset table and load the table of contents; don't 
134
     * care about the offset table since demuxer will calculate those 
135
     * independently */
136
    toc_offset = LE_32(&vmd->vmd_header[812]);
137
    vmd->frame_count = LE_16(&vmd->vmd_header[6]);
138
    url_fseek(pb, toc_offset + vmd->frame_count * 6, SEEK_SET);
139

    
140
    /* each on-disk VMD frame has an audio part and a video part; demuxer
141
     * accounts them separately */
142
    vmd->frame_count *= 2;
143
    raw_frame_table = NULL;
144
    vmd->frame_table = NULL;
145
    raw_frame_table_size = vmd->frame_count * BYTES_PER_FRAME_RECORD;
146
    raw_frame_table = av_malloc(raw_frame_table_size);
147
    vmd->frame_table = av_malloc(vmd->frame_count * sizeof(vmd_frame_t));
148
    if (!raw_frame_table || !vmd->frame_table) {
149
        av_free(raw_frame_table);
150
        av_free(vmd->frame_table);
151
        return AVERROR_NOMEM;
152
    }
153
    if (get_buffer(pb, raw_frame_table, raw_frame_table_size) != 
154
        raw_frame_table_size) {
155
        av_free(raw_frame_table);
156
        av_free(vmd->frame_table);
157
        return -EIO;
158
    }
159

    
160
    current_offset = LE_32(&vmd->vmd_header[20]);
161
    current_frame_record = raw_frame_table;
162
    total_frames = vmd->frame_count;
163
    i = 0;
164
    while (total_frames--) {
165

    
166
        /* if the frame size is 0, do not count the frame and bring the
167
         * total frame count down */
168
        vmd->frame_table[i].frame_size = LE_32(&current_frame_record[2]);
169

    
170
        /* this logic is present so that 0-length audio chunks are not
171
         * accounted */
172
        if (!vmd->frame_table[i].frame_size) {
173
            vmd->frame_count--;  /* one less frame to count */
174
            current_frame_record += BYTES_PER_FRAME_RECORD;
175
            continue;
176
        }
177

    
178
        if (current_frame_record[0] == 0x02)
179
            vmd->frame_table[i].stream_index = vmd->video_stream_index;
180
        else
181
            vmd->frame_table[i].stream_index = vmd->audio_stream_index;
182
        vmd->frame_table[i].frame_offset = current_offset;
183
        current_offset += vmd->frame_table[i].frame_size;
184
        memcpy(vmd->frame_table[i].frame_record, current_frame_record,
185
            BYTES_PER_FRAME_RECORD);
186

    
187
if (current_frame_record[0] == 0x02) {
188
  /* assume 15 fps for now */
189
  vmd->frame_table[i].pts = video_frame_count++;
190
  vmd->frame_table[i].pts *= 90000;
191
  vmd->frame_table[i].pts /= 15;
192
}
193

    
194
        current_frame_record += BYTES_PER_FRAME_RECORD;
195
        i++;
196
    }
197

    
198
    av_free(raw_frame_table);
199

    
200
    /* set the pts reference at 1 pts = 1/90000 sec */
201
    s->pts_num = 1;
202
    s->pts_den = 90000;
203

    
204
    vmd->current_frame = 0;
205

    
206
    return 0;
207
}
208

    
209
static int vmd_read_packet(AVFormatContext *s,
210
                           AVPacket *pkt)
211
{
212
    VmdDemuxContext *vmd = (VmdDemuxContext *)s->priv_data;
213
    ByteIOContext *pb = &s->pb;
214
    int ret = 0;
215
    vmd_frame_t *frame;
216

    
217
    if (vmd->current_frame >= vmd->frame_count)
218
        return -EIO;
219

    
220
    frame = &vmd->frame_table[vmd->current_frame];
221
    /* position the stream (will probably be there already) */
222
    url_fseek(pb, frame->frame_offset, SEEK_SET);
223

    
224
    if (av_new_packet(pkt, frame->frame_size + BYTES_PER_FRAME_RECORD))
225
        return AVERROR_NOMEM;
226
    memcpy(pkt->data, frame->frame_record, BYTES_PER_FRAME_RECORD);
227
    ret = get_buffer(pb, pkt->data + BYTES_PER_FRAME_RECORD, 
228
        frame->frame_size);
229

    
230
    if (ret != frame->frame_size)
231
        ret = -EIO;
232
    pkt->stream_index = frame->stream_index;
233
    pkt->pts = frame->pts;
234

    
235
    vmd->current_frame++;
236

    
237
    return ret;
238
}
239

    
240
static int vmd_read_close(AVFormatContext *s)
241
{
242
    VmdDemuxContext *vmd = (VmdDemuxContext *)s->priv_data;
243

    
244
    av_free(vmd->frame_table);
245

    
246
    return 0;
247
}
248

    
249
static AVInputFormat vmd_iformat = {
250
    "vmd",
251
    "Sierra VMD format",
252
    sizeof(VmdDemuxContext),
253
    vmd_probe,
254
    vmd_read_header,
255
    vmd_read_packet,
256
    vmd_read_close,
257
};
258

    
259
int vmd_init(void)
260
{
261
    av_register_input_format(&vmd_iformat);
262
    return 0;
263
}