vo_xv: Reformat code
[mplayer.git] / libmpdemux / demux_roq.c
blob0dfd7f544bd1959a51cc89281ee0a64ad6092535
1 /*
2 RoQ file demuxer for the MPlayer program
3 by Mike Melanson
4 based on Dr. Tim Ferguson's RoQ document found at:
5 http://www.csse.monash.edu.au/~timf/videocodec.html
6 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
12 #include "config.h"
13 #include "mp_msg.h"
14 #include "help_mp.h"
16 #include "stream/stream.h"
17 #include "demuxer.h"
18 #include "stheader.h"
20 #define RoQ_INFO 0x1001
21 #define RoQ_QUAD_CODEBOOK 0x1002
22 #define RoQ_QUAD_VQ 0x1011
23 #define RoQ_SOUND_MONO 0x1020
24 #define RoQ_SOUND_STEREO 0x1021
26 #define CHUNK_TYPE_AUDIO 0
27 #define CHUNK_TYPE_VIDEO 1
29 typedef struct roq_chunk_t
31 int chunk_type;
32 off_t chunk_offset;
33 int chunk_size;
35 float video_chunk_number; // in the case of a video chunk
36 int running_audio_sample_count; // for an audio chunk
37 } roq_chunk_t;
39 typedef struct roq_data_t
41 int total_chunks;
42 int current_chunk;
43 int total_video_chunks;
44 int total_audio_sample_count;
45 roq_chunk_t *chunks;
46 } roq_data_t;
48 // Check if a stream qualifies as a RoQ file based on the magic numbers
49 // at the start of the file:
50 // 84 10 FF FF FF FF xx xx
51 static int roq_check_file(demuxer_t *demuxer)
53 if ((stream_read_dword(demuxer->stream) == 0x8410FFFF) &&
54 ((stream_read_dword(demuxer->stream) & 0xFFFF0000) == 0xFFFF0000))
55 return DEMUXER_TYPE_ROQ;
56 else
57 return 0;
60 // return value:
61 // 0 = EOF or no stream found
62 // 1 = successfully read a packet
63 static int demux_roq_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds)
65 sh_video_t *sh_video = demuxer->video->sh;
66 roq_data_t *roq_data = (roq_data_t *)demuxer->priv;
67 roq_chunk_t roq_chunk;
69 if (roq_data->current_chunk >= roq_data->total_chunks)
70 return 0;
72 roq_chunk = roq_data->chunks[roq_data->current_chunk];
74 // make sure we're at the right place in the stream and fetch the chunk
75 stream_seek(demuxer->stream, roq_chunk.chunk_offset);
77 if (roq_chunk.chunk_type == CHUNK_TYPE_AUDIO)
78 ds_read_packet(demuxer->audio, demuxer->stream, roq_chunk.chunk_size,
80 roq_chunk.chunk_offset, 0);
81 else
82 ds_read_packet(demuxer->video, demuxer->stream, roq_chunk.chunk_size,
83 roq_chunk.video_chunk_number / sh_video->fps,
84 roq_chunk.chunk_offset, 0);
86 roq_data->current_chunk++;
87 return 1;
90 static demuxer_t* demux_open_roq(demuxer_t* demuxer)
92 sh_video_t *sh_video = NULL;
93 sh_audio_t *sh_audio = NULL;
95 roq_data_t *roq_data = malloc(sizeof(roq_data_t));
96 int chunk_id;
97 int chunk_size;
98 int chunk_arg;
99 int last_chunk_id = 0;
100 int largest_audio_chunk = 0;
101 int fps;
103 roq_data->total_chunks = 0;
104 roq_data->current_chunk = 0;
105 roq_data->total_video_chunks = 0;
106 roq_data->chunks = NULL;
108 // position the stream and start traversing
109 stream_seek(demuxer->stream, 6);
110 fps = stream_read_word_le(demuxer->stream);
111 while (!stream_eof(demuxer->stream))
113 chunk_id = stream_read_word_le(demuxer->stream);
114 chunk_size = stream_read_dword_le(demuxer->stream);
115 chunk_arg = stream_read_word_le(demuxer->stream);
117 // this is the only useful header info in the file
118 if (chunk_id == RoQ_INFO)
120 // there should only be one RoQ_INFO chunk per file
121 if (sh_video)
123 mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Found more than one RoQ_INFO chunk\n");
124 stream_skip(demuxer->stream, 8);
126 else
128 // this is a good opportunity to create a video stream header
129 sh_video = new_sh_video(demuxer, 0);
130 // make sure the demuxer knows about the new stream header
131 demuxer->video->sh = sh_video;
132 // make sure that the video demuxer stream header knows about its
133 // parent video demuxer stream
134 sh_video->ds = demuxer->video;
136 sh_video->disp_w = stream_read_word_le(demuxer->stream);
137 sh_video->disp_h = stream_read_word_le(demuxer->stream);
138 stream_skip(demuxer->stream, 4);
140 // custom fourcc for internal MPlayer use
141 sh_video->format = mmioFOURCC('R', 'o', 'Q', 'V');
143 // constant frame rate
144 sh_video->fps = fps;
145 sh_video->frametime = 1 / sh_video->fps;
148 else if ((chunk_id == RoQ_SOUND_MONO) ||
149 (chunk_id == RoQ_SOUND_STEREO))
151 // create the audio stream header if it hasn't been created it
152 if (sh_audio == NULL)
154 // make the header first
155 sh_audio = new_sh_audio(demuxer, 0);
156 // make sure the demuxer knows about the new stream header
157 demuxer->audio->id = 0;
158 demuxer->audio->sh = sh_audio;
159 // make sure that the audio demuxer stream header knows about its
160 // parent audio demuxer stream
161 sh_audio->ds = demuxer->audio;
163 // go through the bother of making a WAVEFORMATEX structure
164 sh_audio->wf = malloc(sizeof(WAVEFORMATEX));
166 // custom fourcc for internal MPlayer use
167 sh_audio->format = mmioFOURCC('R', 'o', 'Q', 'A');
168 if (chunk_id == RoQ_SOUND_STEREO)
169 sh_audio->wf->nChannels = 2;
170 else
171 sh_audio->wf->nChannels = 1;
172 // always 22KHz, 16-bit
173 sh_audio->wf->nSamplesPerSec = 22050;
174 sh_audio->wf->wBitsPerSample = 16;
177 // index the chunk
178 roq_data->chunks = (roq_chunk_t *)realloc(roq_data->chunks,
179 (roq_data->total_chunks + 1) * sizeof (roq_chunk_t));
180 roq_data->chunks[roq_data->total_chunks].chunk_type = CHUNK_TYPE_AUDIO;
181 roq_data->chunks[roq_data->total_chunks].chunk_offset =
182 stream_tell(demuxer->stream) - 8;
183 roq_data->chunks[roq_data->total_chunks].chunk_size = chunk_size + 8;
184 roq_data->chunks[roq_data->total_chunks].running_audio_sample_count =
185 roq_data->total_audio_sample_count;
187 // audio housekeeping
188 if (chunk_size > largest_audio_chunk)
189 largest_audio_chunk = chunk_size;
190 roq_data->total_audio_sample_count +=
191 (chunk_size / sh_audio->wf->nChannels);
193 stream_skip(demuxer->stream, chunk_size);
194 roq_data->total_chunks++;
196 else if ((chunk_id == RoQ_QUAD_CODEBOOK) ||
197 ((chunk_id == RoQ_QUAD_VQ) && (last_chunk_id != RoQ_QUAD_CODEBOOK)))
199 // index a new chunk if it's a codebook or quad VQ not following a
200 // codebook
201 roq_data->chunks = (roq_chunk_t *)realloc(roq_data->chunks,
202 (roq_data->total_chunks + 1) * sizeof (roq_chunk_t));
203 roq_data->chunks[roq_data->total_chunks].chunk_type = CHUNK_TYPE_VIDEO;
204 roq_data->chunks[roq_data->total_chunks].chunk_offset =
205 stream_tell(demuxer->stream) - 8;
206 roq_data->chunks[roq_data->total_chunks].chunk_size = chunk_size + 8;
207 roq_data->chunks[roq_data->total_chunks].video_chunk_number =
208 roq_data->total_video_chunks++;
210 stream_skip(demuxer->stream, chunk_size);
211 roq_data->total_chunks++;
213 else if ((chunk_id == RoQ_QUAD_VQ) && (last_chunk_id == RoQ_QUAD_CODEBOOK))
215 // if it's a quad VQ chunk following a codebook chunk, extend the last
216 // chunk
217 roq_data->chunks[roq_data->total_chunks - 1].chunk_size += (chunk_size + 8);
218 stream_skip(demuxer->stream, chunk_size);
220 else if (!stream_eof(demuxer->stream))
222 mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Unknown RoQ chunk ID: %04X\n", chunk_id);
225 last_chunk_id = chunk_id;
228 // minimum output buffer size = largest audio chunk * 2, since each byte
229 // in the DPCM encoding effectively represents 1 16-bit sample
230 // (store it in wf->nBlockAlign for the time being since init_audio() will
231 // step on it anyway)
232 if (sh_audio)
233 sh_audio->wf->nBlockAlign = largest_audio_chunk * 2;
235 roq_data->current_chunk = 0;
237 demuxer->priv = roq_data;
239 stream_reset(demuxer->stream);
241 return demuxer;
244 static void demux_close_roq(demuxer_t* demuxer) {
245 roq_data_t *roq_data = demuxer->priv;
247 if(!roq_data)
248 return;
249 free(roq_data);
253 const demuxer_desc_t demuxer_desc_roq = {
254 "RoQ demuxer",
255 "roq",
256 "ROQ",
257 "Mike Melanson",
259 DEMUXER_TYPE_ROQ,
260 0, // unsafe autodetect
261 roq_check_file,
262 demux_roq_fill_buffer,
263 demux_open_roq,
264 demux_close_roq,
265 NULL,
266 NULL