Mercurial > mplayer.hg
comparison libmpdemux/demux_roq.c @ 4451:5627d5b58083
implemented RoQ file demuxing
author | melanson |
---|---|
date | Fri, 01 Feb 2002 05:35:16 +0000 |
parents | |
children | eaa4a2242552 |
comparison
equal
deleted
inserted
replaced
4450:3da8c5706371 | 4451:5627d5b58083 |
---|---|
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 } |