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.
31 #include "stream/stream.h"
35 #define RoQ_INFO 0x1001
36 #define RoQ_QUAD_CODEBOOK 0x1002
37 #define RoQ_QUAD_VQ 0x1011
38 #define RoQ_SOUND_MONO 0x1020
39 #define RoQ_SOUND_STEREO 0x1021
41 #define CHUNK_TYPE_AUDIO 0
42 #define CHUNK_TYPE_VIDEO 1
44 typedef struct roq_chunk_t
50 float video_chunk_number
; // in the case of a video chunk
51 int running_audio_sample_count
; // for an audio chunk
54 typedef struct roq_data_t
58 int total_video_chunks
;
59 int total_audio_sample_count
;
63 // Check if a stream qualifies as a RoQ file based on the magic numbers
64 // at the start of the file:
65 // 84 10 FF FF FF FF xx xx
66 static int roq_check_file(demuxer_t
*demuxer
)
68 if ((stream_read_dword(demuxer
->stream
) == 0x8410FFFF) &&
69 ((stream_read_dword(demuxer
->stream
) & 0xFFFF0000) == 0xFFFF0000))
70 return DEMUXER_TYPE_ROQ
;
76 // 0 = EOF or no stream found
77 // 1 = successfully read a packet
78 static int demux_roq_fill_buffer(demuxer_t
*demuxer
, demux_stream_t
*ds
)
80 sh_video_t
*sh_video
= demuxer
->video
->sh
;
81 roq_data_t
*roq_data
= (roq_data_t
*)demuxer
->priv
;
82 roq_chunk_t roq_chunk
;
84 if (roq_data
->current_chunk
>= roq_data
->total_chunks
)
87 roq_chunk
= roq_data
->chunks
[roq_data
->current_chunk
];
89 // make sure we're at the right place in the stream and fetch the chunk
90 stream_seek(demuxer
->stream
, roq_chunk
.chunk_offset
);
92 if (roq_chunk
.chunk_type
== CHUNK_TYPE_AUDIO
)
93 ds_read_packet(demuxer
->audio
, demuxer
->stream
, roq_chunk
.chunk_size
,
95 roq_chunk
.chunk_offset
, 0);
97 ds_read_packet(demuxer
->video
, demuxer
->stream
, roq_chunk
.chunk_size
,
98 roq_chunk
.video_chunk_number
/ sh_video
->fps
,
99 roq_chunk
.chunk_offset
, 0);
101 roq_data
->current_chunk
++;
105 static demuxer_t
* demux_open_roq(demuxer_t
* demuxer
)
107 sh_video_t
*sh_video
= NULL
;
108 sh_audio_t
*sh_audio
= NULL
;
110 roq_data_t
*roq_data
= malloc(sizeof(roq_data_t
));
114 int last_chunk_id
= 0;
115 int largest_audio_chunk
= 0;
118 roq_data
->total_chunks
= 0;
119 roq_data
->current_chunk
= 0;
120 roq_data
->total_video_chunks
= 0;
121 roq_data
->chunks
= NULL
;
123 // position the stream and start traversing
124 stream_seek(demuxer
->stream
, 6);
125 fps
= stream_read_word_le(demuxer
->stream
);
126 while (!stream_eof(demuxer
->stream
))
128 chunk_id
= stream_read_word_le(demuxer
->stream
);
129 chunk_size
= stream_read_dword_le(demuxer
->stream
);
130 chunk_arg
= stream_read_word_le(demuxer
->stream
);
132 // this is the only useful header info in the file
133 if (chunk_id
== RoQ_INFO
)
135 // there should only be one RoQ_INFO chunk per file
138 mp_msg(MSGT_DECVIDEO
, MSGL_WARN
, "Found more than one RoQ_INFO chunk\n");
139 stream_skip(demuxer
->stream
, 8);
143 // this is a good opportunity to create a video stream header
144 sh_video
= new_sh_video(demuxer
, 0);
145 // make sure the demuxer knows about the new stream header
146 demuxer
->video
->sh
= sh_video
;
147 // make sure that the video demuxer stream header knows about its
148 // parent video demuxer stream
149 sh_video
->ds
= demuxer
->video
;
151 sh_video
->disp_w
= stream_read_word_le(demuxer
->stream
);
152 sh_video
->disp_h
= stream_read_word_le(demuxer
->stream
);
153 stream_skip(demuxer
->stream
, 4);
155 // custom fourcc for internal MPlayer use
156 sh_video
->format
= mmioFOURCC('R', 'o', 'Q', 'V');
158 // constant frame rate
160 sh_video
->frametime
= 1 / sh_video
->fps
;
163 else if ((chunk_id
== RoQ_SOUND_MONO
) ||
164 (chunk_id
== RoQ_SOUND_STEREO
))
166 // create the audio stream header if it hasn't been created it
167 if (sh_audio
== NULL
)
169 // make the header first
170 sh_audio
= new_sh_audio(demuxer
, 0);
171 // make sure the demuxer knows about the new stream header
172 demuxer
->audio
->id
= 0;
173 demuxer
->audio
->sh
= sh_audio
;
174 // make sure that the audio demuxer stream header knows about its
175 // parent audio demuxer stream
176 sh_audio
->ds
= demuxer
->audio
;
178 // go through the bother of making a WAVEFORMATEX structure
179 sh_audio
->wf
= malloc(sizeof(WAVEFORMATEX
));
181 // custom fourcc for internal MPlayer use
182 sh_audio
->format
= mmioFOURCC('R', 'o', 'Q', 'A');
183 if (chunk_id
== RoQ_SOUND_STEREO
)
184 sh_audio
->wf
->nChannels
= 2;
186 sh_audio
->wf
->nChannels
= 1;
187 // always 22KHz, 16-bit
188 sh_audio
->wf
->nSamplesPerSec
= 22050;
189 sh_audio
->wf
->wBitsPerSample
= 16;
193 roq_data
->chunks
= realloc(roq_data
->chunks
,
194 (roq_data
->total_chunks
+ 1) * sizeof (roq_chunk_t
));
195 roq_data
->chunks
[roq_data
->total_chunks
].chunk_type
= CHUNK_TYPE_AUDIO
;
196 roq_data
->chunks
[roq_data
->total_chunks
].chunk_offset
=
197 stream_tell(demuxer
->stream
) - 8;
198 roq_data
->chunks
[roq_data
->total_chunks
].chunk_size
= chunk_size
+ 8;
199 roq_data
->chunks
[roq_data
->total_chunks
].running_audio_sample_count
=
200 roq_data
->total_audio_sample_count
;
202 // audio housekeeping
203 if (chunk_size
> largest_audio_chunk
)
204 largest_audio_chunk
= chunk_size
;
205 roq_data
->total_audio_sample_count
+=
206 (chunk_size
/ sh_audio
->wf
->nChannels
);
208 stream_skip(demuxer
->stream
, chunk_size
);
209 roq_data
->total_chunks
++;
211 else if ((chunk_id
== RoQ_QUAD_CODEBOOK
) ||
212 ((chunk_id
== RoQ_QUAD_VQ
) && (last_chunk_id
!= RoQ_QUAD_CODEBOOK
)))
214 // index a new chunk if it's a codebook or quad VQ not following a
216 roq_data
->chunks
= realloc(roq_data
->chunks
,
217 (roq_data
->total_chunks
+ 1) * sizeof (roq_chunk_t
));
218 roq_data
->chunks
[roq_data
->total_chunks
].chunk_type
= CHUNK_TYPE_VIDEO
;
219 roq_data
->chunks
[roq_data
->total_chunks
].chunk_offset
=
220 stream_tell(demuxer
->stream
) - 8;
221 roq_data
->chunks
[roq_data
->total_chunks
].chunk_size
= chunk_size
+ 8;
222 roq_data
->chunks
[roq_data
->total_chunks
].video_chunk_number
=
223 roq_data
->total_video_chunks
++;
225 stream_skip(demuxer
->stream
, chunk_size
);
226 roq_data
->total_chunks
++;
228 else if ((chunk_id
== RoQ_QUAD_VQ
) && (last_chunk_id
== RoQ_QUAD_CODEBOOK
))
230 // if it's a quad VQ chunk following a codebook chunk, extend the last
232 roq_data
->chunks
[roq_data
->total_chunks
- 1].chunk_size
+= (chunk_size
+ 8);
233 stream_skip(demuxer
->stream
, chunk_size
);
235 else if (!stream_eof(demuxer
->stream
))
237 mp_msg(MSGT_DECVIDEO
, MSGL_WARN
, "Unknown RoQ chunk ID: %04X\n", chunk_id
);
240 last_chunk_id
= chunk_id
;
243 // minimum output buffer size = largest audio chunk * 2, since each byte
244 // in the DPCM encoding effectively represents 1 16-bit sample
245 // (store it in wf->nBlockAlign for the time being since init_audio() will
246 // step on it anyway)
248 sh_audio
->wf
->nBlockAlign
= largest_audio_chunk
* 2;
250 roq_data
->current_chunk
= 0;
252 demuxer
->priv
= roq_data
;
254 stream_reset(demuxer
->stream
);
259 static void demux_close_roq(demuxer_t
* demuxer
) {
260 roq_data_t
*roq_data
= demuxer
->priv
;
268 const demuxer_desc_t demuxer_desc_roq
= {
275 0, // unsafe autodetect
277 demux_roq_fill_buffer
,