4451
|
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 */
|
|
7
|
|
8 #include <stdio.h>
|
|
9 #include <stdlib.h>
|
|
10 #include <unistd.h>
|
|
11
|
|
12 #include "config.h"
|
|
13 #include "mp_msg.h"
|
|
14 #include "help_mp.h"
|
|
15
|
|
16 #include "stream.h"
|
|
17 #include "demuxer.h"
|
|
18 #include "stheader.h"
|
|
19
|
|
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
|
|
25
|
|
26 #define CHUNK_TYPE_AUDIO 0
|
|
27 #define CHUNK_TYPE_VIDEO 1
|
|
28
|
|
29 #define RoQ_FPS 24
|
|
30
|
|
31 typedef struct roq_chunk_t
|
|
32 {
|
|
33 int chunk_type;
|
|
34 off_t chunk_offset;
|
|
35 int chunk_size;
|
|
36 } roq_chunk_t;
|
|
37
|
|
38 typedef struct roq_data_t
|
|
39 {
|
|
40 int total_chunks;
|
|
41 int current_chunk;
|
|
42 roq_chunk_t *chunks;
|
|
43 } roq_data_t;
|
|
44
|
|
45 // Check if a stream qualifies as a RoQ file based on the magic numbers
|
|
46 // at the start of the file:
|
|
47 // 84 10 FF FF FF FF 1E 00
|
|
48 int roq_check_file(demuxer_t *demuxer)
|
|
49 {
|
|
50 stream_reset(demuxer->stream);
|
|
51 stream_seek(demuxer->stream, 0);
|
|
52
|
|
53 if ((stream_read_dword(demuxer->stream) == 0x8410FFFF) &&
|
|
54 (stream_read_dword(demuxer->stream) == 0xFFFF1E00))
|
|
55 return 1;
|
|
56 else
|
|
57 return 0;
|
|
58 }
|
|
59
|
|
60 // return value:
|
|
61 // 0 = EOF or no stream found
|
|
62 // 1 = successfully read a packet
|
|
63 int demux_roq_fill_buffer(demuxer_t *demuxer)
|
|
64 {
|
|
65 roq_data_t *roq_data = (roq_data_t *)demuxer->priv;
|
|
66 roq_chunk_t roq_chunk;
|
|
67 demux_stream_t *ds;
|
|
68
|
|
69 if (roq_data->current_chunk >= roq_data->total_chunks)
|
|
70 return 0;
|
|
71
|
|
72 roq_chunk = roq_data->chunks[roq_data->current_chunk];
|
|
73 if (roq_chunk.chunk_type == CHUNK_TYPE_AUDIO)
|
|
74 ds = demuxer->audio;
|
|
75 else
|
|
76 ds = demuxer->video;
|
|
77
|
|
78 // make sure we're at the right place in the stream and fetch the chunk
|
|
79 stream_seek(demuxer->stream, roq_chunk.chunk_offset);
|
|
80 ds_read_packet(
|
|
81 ds,
|
|
82 demuxer->stream,
|
|
83 roq_chunk.chunk_size,
|
|
84 // roq_data->current_frame/sh_video->fps,
|
|
85 0,
|
|
86 roq_chunk.chunk_offset,
|
|
87 0
|
|
88 );
|
|
89
|
|
90 roq_data->current_chunk++;
|
|
91 return 1;
|
|
92 }
|
|
93
|
|
94 demuxer_t* demux_open_roq(demuxer_t* demuxer)
|
|
95 {
|
|
96 sh_video_t *sh_video = NULL;
|
|
97 sh_audio_t *sh_audio = NULL;
|
|
98
|
|
99 roq_data_t *roq_data = (roq_data_t *)malloc(sizeof(roq_data_t));
|
|
100 int chunk_id;
|
|
101 int chunk_size;
|
|
102 int chunk_arg1;
|
|
103 int chunk_arg2;
|
|
104 int chunk_counter = 0;
|
|
105 int last_chunk_id = 0;
|
|
106
|
|
107 roq_data->chunks = NULL;
|
|
108
|
|
109 // position the stream and start traversing
|
|
110 stream_seek(demuxer->stream, 8);
|
|
111 while (!stream_eof(demuxer->stream))
|
|
112 {
|
|
113 chunk_id = stream_read_word_le(demuxer->stream);
|
|
114 chunk_size = stream_read_word_le(demuxer->stream);
|
|
115 chunk_arg1 = stream_read_word_le(demuxer->stream);
|
|
116 chunk_arg2 = stream_read_word_le(demuxer->stream);
|
|
117
|
|
118 // this is the only useful header info in the file
|
|
119 if (chunk_id == RoQ_INFO)
|
|
120 {
|
|
121 // there should only be one RoQ_INFO chunk per file
|
|
122 if (sh_video)
|
|
123 {
|
|
124 mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Found more than one RoQ_INFO chunk\n");
|
|
125 stream_skip(demuxer->stream, 8);
|
|
126 }
|
|
127 else
|
|
128 {
|
|
129 // make the header first
|
|
130 sh_video = new_sh_video(demuxer, 0);
|
|
131 // make sure the demuxer knows about the new stream header
|
|
132 demuxer->video->sh = sh_video;
|
|
133 // make sure that the video demuxer stream header knows about its
|
|
134 // parent video demuxer stream
|
|
135 sh_video->ds = demuxer->video;
|
|
136
|
|
137 // this is a good opportunity to create a video stream header
|
|
138 sh_video->disp_w = stream_read_word_le(demuxer->stream);
|
|
139 sh_video->disp_h = stream_read_word_le(demuxer->stream);
|
|
140 stream_skip(demuxer->stream, 4);
|
|
141
|
|
142 // custom fourcc for internal MPlayer use
|
|
143 sh_video->format = mmioFOURCC('R', 'o', 'Q', 'V');
|
|
144
|
|
145 // constant frame rate
|
|
146 sh_video->fps = RoQ_FPS;
|
|
147 sh_video->frametime = 1 / RoQ_FPS;
|
|
148 }
|
|
149 }
|
|
150 else if ((chunk_id == RoQ_SOUND_MONO) ||
|
|
151 (chunk_id == RoQ_SOUND_STEREO))
|
|
152 {
|
|
153 // create the audio stream header if it hasn't been created it
|
|
154 if (sh_audio == NULL)
|
|
155 {
|
|
156 // make the header first
|
|
157 sh_audio = new_sh_audio(demuxer, 0);
|
|
158 // make sure the demuxer knows about the new stream header
|
|
159 demuxer->audio->sh = sh_audio;
|
|
160 // make sure that the audio demuxer stream header knows about its
|
|
161 // parent audio demuxer stream
|
|
162 sh_audio->ds = demuxer->audio;
|
|
163
|
|
164 // custom fourcc for internal MPlayer use
|
|
165 sh_audio->format = mmioFOURCC('R', 'o', 'Q', 'A');
|
|
166 // assume it's mono until there's reason to believe otherwise
|
|
167 sh_audio->channels = 1;
|
|
168 // always 22KHz, 16-bit
|
|
169 sh_audio->samplerate = 22050;
|
|
170 sh_audio->samplesize = 2;
|
|
171 }
|
|
172
|
|
173 // if it's stereo, promote the channel number
|
|
174 if (chunk_id == RoQ_SOUND_STEREO)
|
|
175 sh_audio->channels = 2;
|
|
176
|
|
177 // index the chunk
|
|
178 roq_data->chunks = (roq_chunk_t *)realloc(roq_data->chunks,
|
|
179 (chunk_counter + 1) * sizeof (roq_chunk_t));
|
|
180 roq_data->chunks[chunk_counter].chunk_type = CHUNK_TYPE_AUDIO;
|
|
181 roq_data->chunks[chunk_counter].chunk_offset =
|
|
182 stream_tell(demuxer->stream) - 8;
|
|
183 roq_data->chunks[chunk_counter].chunk_size = chunk_size + 8;
|
|
184
|
|
185 stream_skip(demuxer->stream, chunk_size);
|
|
186 chunk_counter++;
|
|
187 }
|
|
188 else if ((chunk_id == RoQ_QUAD_CODEBOOK) ||
|
|
189 ((chunk_id == RoQ_QUAD_VQ) && (last_chunk_id != RoQ_QUAD_CODEBOOK)))
|
|
190 {
|
|
191 // index a new chunk if it's a codebook or quad VQ not following a
|
|
192 // codebook
|
|
193 roq_data->chunks = (roq_chunk_t *)realloc(roq_data->chunks,
|
|
194 (chunk_counter + 1) * sizeof (roq_chunk_t));
|
|
195 roq_data->chunks[chunk_counter].chunk_type = CHUNK_TYPE_VIDEO;
|
|
196 roq_data->chunks[chunk_counter].chunk_offset =
|
|
197 stream_tell(demuxer->stream) - 8;
|
|
198 roq_data->chunks[chunk_counter].chunk_size = chunk_size + 8;
|
|
199
|
|
200 stream_skip(demuxer->stream, chunk_size);
|
|
201 chunk_counter++;
|
|
202 }
|
|
203 else if ((chunk_id == RoQ_QUAD_VQ) && (last_chunk_id == RoQ_QUAD_CODEBOOK))
|
|
204 {
|
|
205 // if it's a quad VQ chunk following a codebook chunk, extend the last
|
|
206 // chunk
|
|
207 roq_data->chunks[chunk_counter - 1].chunk_size += (chunk_size + 8);
|
|
208 stream_skip(demuxer->stream, chunk_size);
|
|
209 }
|
|
210 else
|
|
211 {
|
|
212 mp_msg(MSGT_DECVIDEO, MSGL_WARN, "Unknown RoQ chunk ID: %04X\n", chunk_id);
|
|
213 }
|
|
214
|
|
215 last_chunk_id = chunk_id;
|
|
216 }
|
|
217
|
|
218 roq_data->total_chunks = chunk_counter;
|
|
219 roq_data->current_chunk = 0;
|
|
220
|
|
221 demuxer->priv = roq_data;
|
|
222
|
|
223 return demuxer;
|
|
224 }
|