2 RoQ file demuxer for the MPlayer program
4 based on Dr. Tim Ferguson's RoQ document found at:
5 http://www.csse.monash.edu.au/~timf/videocodec.html
16 #include "stream/stream.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
35 float video_chunk_number
; // in the case of a video chunk
36 int running_audio_sample_count
; // for an audio chunk
39 typedef struct roq_data_t
43 int total_video_chunks
;
44 int total_audio_sample_count
;
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
;
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
)
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);
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
++;
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
));
99 int last_chunk_id
= 0;
100 int largest_audio_chunk
= 0;
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
123 mp_msg(MSGT_DECVIDEO
, MSGL_WARN
, "Found more than one RoQ_INFO chunk\n");
124 stream_skip(demuxer
->stream
, 8);
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
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;
171 sh_audio
->wf
->nChannels
= 1;
172 // always 22KHz, 16-bit
173 sh_audio
->wf
->nSamplesPerSec
= 22050;
174 sh_audio
->wf
->wBitsPerSample
= 16;
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
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
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)
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
);
244 static void demux_close_roq(demuxer_t
* demuxer
) {
245 roq_data_t
*roq_data
= demuxer
->priv
;
253 const demuxer_desc_t demuxer_desc_roq
= {
260 0, // unsafe autodetect
262 demux_roq_fill_buffer
,