sd_ass: initialize structs for external tracks properly
[mplayer.git] / libmpdemux / demux_roq.c
blobaf9e4c5a8e75857bb90ae0872b1a4ac0028032b5
1 /*
2 * RoQ file demuxer
3 * copyright (c) 2002 Mike Melanson
4 * based on Dr. Tim Ferguson's RoQ document found at:
5 * http://www.csse.monash.edu.au/~timf/videocodec.html
7 * This file is part of MPlayer.
9 * MPlayer is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * MPlayer is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
28 #include "config.h"
29 #include "mp_msg.h"
31 #include "stream/stream.h"
32 #include "demuxer.h"
33 #include "stheader.h"
34 #include "libavutil/attributes.h"
36 #define RoQ_INFO 0x1001
37 #define RoQ_QUAD_CODEBOOK 0x1002
38 #define RoQ_QUAD_VQ 0x1011
39 #define RoQ_SOUND_MONO 0x1020
40 #define RoQ_SOUND_STEREO 0x1021
42 #define CHUNK_TYPE_AUDIO 0
43 #define CHUNK_TYPE_VIDEO 1
45 typedef struct roq_chunk_t
47 int chunk_type;
48 off_t chunk_offset;
49 int chunk_size;
51 float video_chunk_number; // in the case of a video chunk
52 int running_audio_sample_count; // for an audio chunk
53 } roq_chunk_t;
55 typedef struct roq_data_t
57 int total_chunks;
58 int current_chunk;
59 int total_video_chunks;
60 int total_audio_sample_count;
61 roq_chunk_t *chunks;
62 } roq_data_t;
64 // Check if a stream qualifies as a RoQ file based on the magic numbers
65 // at the start of the file:
66 // 84 10 FF FF FF FF xx xx
67 static int roq_check_file(demuxer_t *demuxer)
69 if ((stream_read_dword(demuxer->stream) == 0x8410FFFF) &&
70 ((stream_read_dword(demuxer->stream) & 0xFFFF0000) == 0xFFFF0000))
71 return DEMUXER_TYPE_ROQ;
72 else
73 return 0;
76 // return value:
77 // 0 = EOF or no stream found
78 // 1 = successfully read a packet
79 static int demux_roq_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
81 sh_video_t *sh_video = demuxer->video->sh;
82 roq_data_t *roq_data = (roq_data_t *)demuxer->priv;
83 roq_chunk_t roq_chunk;
85 if (roq_data->current_chunk >= roq_data->total_chunks)
86 return 0;
88 roq_chunk = roq_data->chunks[roq_data->current_chunk];
90 // make sure we're at the right place in the stream and fetch the chunk
91 stream_seek(demuxer->stream, roq_chunk.chunk_offset);
93 if (roq_chunk.chunk_type == CHUNK_TYPE_AUDIO)
94 ds_read_packet(demuxer->audio, demuxer->stream, roq_chunk.chunk_size,
96 roq_chunk.chunk_offset, 0);
97 else
98 ds_read_packet(demuxer->video, demuxer->stream, roq_chunk.chunk_size,
99 roq_chunk.video_chunk_number / sh_video->fps,
100 roq_chunk.chunk_offset, 0);
102 roq_data->current_chunk++;
103 return 1;
106 static demuxer_t* demux_open_roq(demuxer_t* demuxer)
108 sh_video_t *sh_video = NULL;
109 sh_audio_t *sh_audio = NULL;
111 roq_data_t *roq_data = malloc(sizeof(roq_data_t));
112 int chunk_id;
113 int chunk_size;
114 int chunk_arg av_unused;
115 int last_chunk_id = 0;
116 int largest_audio_chunk = 0;
117 int fps;
119 roq_data->total_chunks = 0;
120 roq_data->current_chunk = 0;
121 roq_data->total_video_chunks = 0;
122 roq_data->chunks = NULL;
124 // position the stream and start traversing
125 stream_seek(demuxer->stream, 6);
126 fps = stream_read_word_le(demuxer->stream);
127 while (!stream_eof(demuxer->stream))
129 chunk_id = stream_read_word_le(demuxer->stream);
130 chunk_size = stream_read_dword_le(demuxer->stream);
131 chunk_arg = stream_read_word_le(demuxer->stream);
133 // this is the only useful header info in the file
134 if (chunk_id == RoQ_INFO)
136 // there should only be one RoQ_INFO chunk per file
137 if (sh_video)
139 mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Found more than one RoQ_INFO chunk\n");
140 stream_skip(demuxer->stream, 8);
142 else
144 // this is a good opportunity to create a video stream header
145 sh_video = new_sh_video(demuxer, 0);
146 // make sure the demuxer knows about the new stream header
147 demuxer->video->sh = sh_video;
148 // make sure that the video demuxer stream header knows about its
149 // parent video demuxer stream
150 sh_video->ds = demuxer->video;
152 sh_video->disp_w = stream_read_word_le(demuxer->stream);
153 sh_video->disp_h = stream_read_word_le(demuxer->stream);
154 stream_skip(demuxer->stream, 4);
156 // custom fourcc for internal MPlayer use
157 sh_video->format = mmioFOURCC('R', 'o', 'Q', 'V');
159 // constant frame rate
160 sh_video->fps = fps;
161 sh_video->frametime = 1 / sh_video->fps;
164 else if ((chunk_id == RoQ_SOUND_MONO) ||
165 (chunk_id == RoQ_SOUND_STEREO))
167 // create the audio stream header if it hasn't been created it
168 if (sh_audio == NULL)
170 // make the header first
171 sh_audio = new_sh_audio(demuxer, 0);
172 // make sure the demuxer knows about the new stream header
173 demuxer->audio->id = 0;
174 demuxer->audio->sh = sh_audio;
175 // make sure that the audio demuxer stream header knows about its
176 // parent audio demuxer stream
177 sh_audio->ds = demuxer->audio;
179 // go through the bother of making a WAVEFORMATEX structure
180 sh_audio->wf = malloc(sizeof(*sh_audio->wf));
182 // custom fourcc for internal MPlayer use
183 sh_audio->format = mmioFOURCC('R', 'o', 'Q', 'A');
184 if (chunk_id == RoQ_SOUND_STEREO)
185 sh_audio->wf->nChannels = 2;
186 else
187 sh_audio->wf->nChannels = 1;
188 // always 22KHz, 16-bit
189 sh_audio->wf->nSamplesPerSec = 22050;
190 sh_audio->wf->wBitsPerSample = 16;
193 // index the chunk
194 roq_data->chunks = realloc(roq_data->chunks,
195 (roq_data->total_chunks + 1) * sizeof (roq_chunk_t));
196 roq_data->chunks[roq_data->total_chunks].chunk_type = CHUNK_TYPE_AUDIO;
197 roq_data->chunks[roq_data->total_chunks].chunk_offset =
198 stream_tell(demuxer->stream) - 8;
199 roq_data->chunks[roq_data->total_chunks].chunk_size = chunk_size + 8;
200 roq_data->chunks[roq_data->total_chunks].running_audio_sample_count =
201 roq_data->total_audio_sample_count;
203 // audio housekeeping
204 if (chunk_size > largest_audio_chunk)
205 largest_audio_chunk = chunk_size;
206 roq_data->total_audio_sample_count +=
207 (chunk_size / sh_audio->wf->nChannels);
209 stream_skip(demuxer->stream, chunk_size);
210 roq_data->total_chunks++;
212 else if ((chunk_id == RoQ_QUAD_CODEBOOK) ||
213 ((chunk_id == RoQ_QUAD_VQ) && (last_chunk_id != RoQ_QUAD_CODEBOOK)))
215 // index a new chunk if it's a codebook or quad VQ not following a
216 // codebook
217 roq_data->chunks = realloc(roq_data->chunks,
218 (roq_data->total_chunks + 1) * sizeof (roq_chunk_t));
219 roq_data->chunks[roq_data->total_chunks].chunk_type = CHUNK_TYPE_VIDEO;
220 roq_data->chunks[roq_data->total_chunks].chunk_offset =
221 stream_tell(demuxer->stream) - 8;
222 roq_data->chunks[roq_data->total_chunks].chunk_size = chunk_size + 8;
223 roq_data->chunks[roq_data->total_chunks].video_chunk_number =
224 roq_data->total_video_chunks++;
226 stream_skip(demuxer->stream, chunk_size);
227 roq_data->total_chunks++;
229 else if ((chunk_id == RoQ_QUAD_VQ) && (last_chunk_id == RoQ_QUAD_CODEBOOK))
231 // if it's a quad VQ chunk following a codebook chunk, extend the last
232 // chunk
233 roq_data->chunks[roq_data->total_chunks - 1].chunk_size += (chunk_size + 8);
234 stream_skip(demuxer->stream, chunk_size);
236 else if (!stream_eof(demuxer->stream))
238 mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Unknown RoQ chunk ID: %04X\n", chunk_id);
241 last_chunk_id = chunk_id;
244 // minimum output buffer size = largest audio chunk * 2, since each byte
245 // in the DPCM encoding effectively represents 1 16-bit sample
246 // (store it in wf->nBlockAlign for the time being since init_audio() will
247 // step on it anyway)
248 if (sh_audio)
249 sh_audio->wf->nBlockAlign = largest_audio_chunk * 2;
251 roq_data->current_chunk = 0;
253 demuxer->priv = roq_data;
255 stream_reset(demuxer->stream);
257 return demuxer;
260 static void demux_close_roq(demuxer_t* demuxer) {
261 roq_data_t *roq_data = demuxer->priv;
263 free(roq_data);
267 const demuxer_desc_t demuxer_desc_roq = {
268 "RoQ demuxer",
269 "roq",
270 "ROQ",
271 "Mike Melanson",
273 DEMUXER_TYPE_ROQ,
274 0, // unsafe autodetect
275 roq_check_file,
276 demux_roq_fill_buffer,
277 demux_open_roq,
278 demux_close_roq,
279 NULL,
280 NULL