3777
|
1 /*
|
|
2 Real parser & demuxer
|
|
3
|
|
4 (C) Alex Beregszaszi <alex@naxine.org>
|
|
5
|
|
6 Based on FFmpeg's libav/rm.c.
|
|
7 */
|
|
8
|
|
9 #include <stdio.h>
|
|
10 #include <stdlib.h>
|
|
11 #include <unistd.h>
|
|
12
|
|
13 #include "config.h"
|
|
14 #include "mp_msg.h"
|
|
15 #include "help_mp.h"
|
|
16
|
|
17 #include "stream.h"
|
|
18 #include "demuxer.h"
|
|
19 #include "stheader.h"
|
|
20 #include "bswap.h"
|
|
21
|
|
22 #define MKTAG(a, b, c, d) (a | (b << 8) | (c << 16) | (d << 24))
|
|
23
|
|
24 #define MAX_STREAMS 10
|
|
25
|
|
26 typedef struct {
|
|
27 int num_of_packets;
|
|
28 int last_a_stream;
|
|
29 int a_streams[MAX_STREAMS];
|
|
30 int last_v_stream;
|
|
31 int v_streams[MAX_STREAMS];
|
|
32 } real_priv_t;
|
|
33
|
|
34 static void get_str(int isbyte, demuxer_t *demuxer, char *buf, int buf_size)
|
|
35 {
|
|
36 int len, i;
|
|
37 char *q;
|
|
38
|
|
39 if (isbyte)
|
|
40 len = stream_read_char(demuxer->stream);
|
|
41 else
|
|
42 len = stream_read_word(demuxer->stream);
|
|
43 #if 1
|
|
44 q = buf;
|
|
45 for (i = 0; i < len; i++)
|
|
46 if (i < (buf_size - 1))
|
|
47 *q++ = stream_read_char(demuxer->stream);
|
|
48 *q = 0;
|
|
49 #else
|
|
50 stream_read(demuxer->stream, buf, buf_size);
|
|
51 #endif
|
|
52 }
|
|
53
|
|
54 static void skip_str(int isbyte, demuxer_t *demuxer)
|
|
55 {
|
|
56 int len;
|
|
57
|
|
58 if (isbyte)
|
|
59 len = stream_read_char(demuxer->stream);
|
|
60 else
|
|
61 len = stream_read_word(demuxer->stream);
|
|
62
|
|
63 stream_skip(demuxer->stream, len);
|
|
64
|
|
65 printf("skip_str: %d bytes skipped\n", len);
|
|
66 }
|
|
67
|
|
68 int real_check_file(demuxer_t* demuxer){
|
|
69 real_priv_t *priv;
|
|
70 int c;
|
|
71
|
|
72 mp_msg(MSGT_DEMUX,MSGL_V,"Checking for REAL\n");
|
|
73
|
|
74 c = stream_read_dword_le(demuxer->stream);
|
|
75 if (c == -256)
|
|
76 return 0; /* EOF */
|
|
77 if (c != MKTAG('.', 'R', 'M', 'F'))
|
|
78 return 0; /* bad magic */
|
|
79
|
|
80 priv = malloc(sizeof(real_priv_t));
|
|
81 memset(priv, 0, sizeof(real_priv_t));
|
|
82 demuxer->priv = priv;
|
|
83
|
|
84 return 1;
|
|
85 }
|
|
86
|
|
87 // return value:
|
|
88 // 0 = EOF or no stream found
|
|
89 // 1 = successfully read a packet
|
|
90 int demux_real_fill_buffer(demuxer_t *demuxer)
|
|
91 {
|
|
92 real_priv_t *priv = demuxer->priv;
|
|
93 demux_stream_t *ds = NULL;
|
|
94 int len;
|
|
95 int timestamp;
|
|
96 int stream_id;
|
|
97 int i;
|
|
98
|
|
99 // printf("num_of_packets: %d\n", priv->num_of_packets);
|
|
100
|
|
101 loop:
|
|
102 if (priv->num_of_packets == 0)
|
|
103 return 0; /* EOF */
|
|
104 stream_skip(demuxer->stream, 2);
|
|
105 len = stream_read_word(demuxer->stream);
|
|
106 if (len == -256) /* EOF */
|
|
107 return 0;
|
|
108 if (len < 12)
|
|
109 {
|
|
110 printf("bad packet len (%d)\n", len);
|
|
111 stream_skip(demuxer->stream, len);
|
|
112 goto loop;
|
|
113 // return 0; /* bad packet */
|
|
114 }
|
|
115 stream_id = stream_read_word(demuxer->stream);
|
|
116 timestamp = stream_read_dword(demuxer->stream);
|
|
117
|
|
118 // printf("packet: len: %d, stream_id: %d, timestamp: %d\n",
|
|
119 // len, stream_id, timestamp);
|
|
120
|
|
121 stream_skip(demuxer->stream, 1); /* reserved */
|
|
122 stream_skip(demuxer->stream, 1); /* flags */
|
|
123
|
|
124 priv->num_of_packets--;
|
|
125 len -= 12;
|
|
126
|
|
127 /* check if stream_id is audio stream */
|
|
128 for (i = 0; i < priv->last_a_stream; i++)
|
|
129 {
|
|
130 if (priv->a_streams[i] == stream_id)
|
|
131 {
|
|
132 // printf("packet is audio (id: %d)\n", stream_id);
|
|
133 ds = demuxer->audio; /* FIXME */
|
|
134 break;
|
|
135 }
|
|
136 }
|
|
137 /* check if stream_id is video stream */
|
|
138 for (i = 0; i < priv->last_v_stream; i++)
|
|
139 {
|
|
140 if (priv->v_streams[i] == stream_id)
|
|
141 {
|
|
142 // printf("packet is video (id: %d)\n", stream_id);
|
|
143 ds = demuxer->video; /* FIXME */
|
|
144 break;
|
|
145 }
|
|
146 }
|
|
147
|
|
148 /* id not found */
|
|
149 if (ds == NULL)
|
|
150 {
|
|
151 printf("unknown stream id (%d)\n", stream_id);
|
|
152 stream_skip(demuxer->stream, len);
|
|
153 goto loop;
|
|
154 }
|
|
155
|
|
156 demuxer->filepos = stream_tell(demuxer->stream);
|
|
157 ds_read_packet(ds, demuxer->stream, len, timestamp/90000.0f, demuxer->filepos, 0);
|
|
158
|
|
159 return 1;
|
|
160 }
|
|
161
|
|
162 void demux_open_real(demuxer_t* demuxer)
|
|
163 {
|
|
164 real_priv_t* priv = demuxer->priv;
|
|
165 int num_of_headers;
|
|
166 int i;
|
|
167
|
|
168 stream_skip(demuxer->stream, 4); /* header size */
|
|
169 stream_skip(demuxer->stream, 2);
|
|
170 stream_skip(demuxer->stream, 4);
|
|
171 num_of_headers = stream_read_dword(demuxer->stream);
|
|
172 // stream_skip(demuxer->stream, 4); /* number of headers */
|
|
173
|
|
174 /* parse chunks */
|
|
175 for (i = 1; i < num_of_headers; i++)
|
|
176 {
|
|
177 int chunk, chunk_size;
|
|
178
|
|
179 chunk = stream_read_dword_le(demuxer->stream);
|
|
180 chunk_size = stream_read_dword(demuxer->stream);
|
|
181
|
|
182 stream_skip(demuxer->stream, 2);
|
|
183
|
|
184 if (chunk_size < 10)
|
|
185 goto fail;
|
|
186
|
|
187 printf("Chunk: %.4s (size: %d)\n",
|
|
188 (char *)&chunk, chunk_size);
|
|
189
|
|
190 switch(chunk)
|
|
191 {
|
|
192 case MKTAG('P', 'R', 'O', 'P'):
|
|
193 printf("Properties chunk\n");
|
|
194 stream_skip(demuxer->stream, 4); /* max bitrate */
|
|
195 stream_skip(demuxer->stream, 4); /* avg bitrate */
|
|
196 stream_skip(demuxer->stream, 4); /* max packet size */
|
|
197 stream_skip(demuxer->stream, 4); /* avg packet size */
|
|
198 stream_skip(demuxer->stream, 4); /* nb packets */
|
|
199 stream_skip(demuxer->stream, 4); /* duration */
|
|
200 stream_skip(demuxer->stream, 4); /* preroll */
|
|
201 stream_skip(demuxer->stream, 4); /* index offset */
|
|
202 stream_skip(demuxer->stream, 4); /* data offset */
|
|
203 stream_skip(demuxer->stream, 2); /* nb streams */
|
|
204 stream_skip(demuxer->stream, 2); /* flags */
|
|
205 break;
|
|
206 case MKTAG('C', 'O', 'N', 'T'):
|
|
207 printf("Broadcasting informations (title, author, copyright, comment)\n");
|
|
208 skip_str(0, demuxer); /* title */
|
|
209 skip_str(0, demuxer); /* author */
|
|
210 skip_str(0, demuxer); /* copyright */
|
|
211 skip_str(0, demuxer); /* comment */
|
|
212 break;
|
|
213 case MKTAG('M', 'D', 'P', 'R'):
|
|
214 {
|
|
215 /* new stream */
|
|
216 int stream_id;
|
|
217 int bitrate;
|
|
218 int codec_data_size;
|
|
219 int codec_pos;
|
|
220 int v;
|
|
221
|
|
222 stream_id = stream_read_word(demuxer->stream);
|
|
223 printf("Found new stream (id: %d)\n", stream_id);
|
|
224
|
|
225 stream_skip(demuxer->stream, 4); /* max bitrate */
|
|
226 bitrate = stream_read_dword(demuxer->stream); /* bitrate */
|
|
227 stream_skip(demuxer->stream, 4); /* max packet size */
|
|
228 stream_skip(demuxer->stream, 4); /* avg packet size */
|
|
229 stream_skip(demuxer->stream, 4); /* start time */
|
|
230 stream_skip(demuxer->stream, 4); /* preroll */
|
|
231 stream_skip(demuxer->stream, 4); /* duration */
|
|
232
|
|
233 skip_str(1, demuxer); /* stream description */
|
|
234 skip_str(1, demuxer); /* mimetype */
|
|
235
|
|
236 codec_data_size = stream_read_dword(demuxer->stream);
|
|
237 codec_pos = stream_tell(demuxer->stream);
|
|
238
|
|
239 v = stream_read_dword(demuxer->stream);
|
|
240 if (v == MKTAG(0xfd, 'a', 'r', '.')) /* audio header */
|
|
241 {
|
|
242 sh_audio_t *sh = new_sh_audio(demuxer, stream_id);
|
|
243
|
|
244 printf("Found audio stream!\n");
|
|
245 stream_skip(demuxer->stream, 4); /* version */
|
|
246 stream_skip(demuxer->stream, 4); /* .ra4 */
|
|
247 stream_skip(demuxer->stream, 4);
|
|
248 stream_skip(demuxer->stream, 2);
|
|
249 stream_skip(demuxer->stream, 4); /* header size */
|
|
250 stream_skip(demuxer->stream, 2); /* add codec info */
|
|
251 stream_skip(demuxer->stream, 4); /* coded frame size */
|
|
252 stream_skip(demuxer->stream, 4);
|
|
253 stream_skip(demuxer->stream, 4);
|
|
254 stream_skip(demuxer->stream, 4);
|
|
255 stream_skip(demuxer->stream, 2); /* 1 */
|
|
256 stream_skip(demuxer->stream, 2); /* coded frame size */
|
|
257 stream_skip(demuxer->stream, 4);
|
|
258
|
|
259 sh->samplerate = stream_read_word(demuxer->stream);
|
|
260 stream_skip(demuxer->stream, 4);
|
|
261 sh->channels = stream_read_word(demuxer->stream);
|
|
262
|
|
263 {
|
|
264 char buf[128];
|
|
265
|
|
266 get_str(1, demuxer, buf, sizeof(buf));
|
|
267 get_str(1, demuxer, buf, sizeof(buf));
|
|
268
|
|
269 v = 0; /* not supported audio codec */
|
|
270 if (!strcmp(buf, "dnet"))
|
|
271 {
|
|
272 printf("Found AC3 audio\n");
|
|
273 sh->format = 0x2000; /* ac3 */
|
|
274 v = 1;
|
|
275 }
|
|
276 }
|
|
277 // skip_str(1, demuxer); /* desc */
|
|
278 // skip_str(1, demuxer); /* desc */
|
|
279
|
|
280 /* the audio codec name is stored in desc */
|
|
281 /* available codecs: DNET -> ac3 */
|
|
282 // sh->format = 0x2000; /* ac3 */
|
|
283
|
|
284 if (v)
|
|
285 {
|
|
286 printf("Audio codec: 0x%x\n", sh->format);
|
|
287 /* insert as stream */
|
|
288 demuxer->audio->sh = sh;
|
|
289 sh->ds = demuxer->audio;
|
|
290 demuxer->audio->id = stream_id;
|
|
291
|
|
292 if (priv->last_a_stream+1 < MAX_STREAMS)
|
|
293 {
|
|
294 priv->a_streams[priv->last_a_stream] = stream_id;
|
|
295 priv->last_a_stream++;
|
|
296 }
|
|
297 }
|
|
298 }
|
|
299 else
|
|
300 {
|
|
301 /* video header */
|
|
302 sh_video_t *sh = new_sh_video(demuxer, stream_id);
|
|
303
|
|
304 v = stream_read_dword_le(demuxer->stream);
|
|
305 printf("video: %.4s (%x)\n", (char *)&v, v);
|
|
306 if (v != MKTAG('V', 'I', 'D', 'O'))
|
|
307 {
|
|
308 mp_msg(MSGT_DEMUX, MSGL_ERR, "Unsupported video codec\n");
|
|
309 goto skip_this_chunk;
|
|
310 }
|
|
311
|
|
312 sh->format = stream_read_dword_le(demuxer->stream); /* fourcc */
|
|
313 printf("video fourcc: %.4s (%x)\n", (char *)&sh->format, sh->format);
|
|
314
|
|
315 // emulate BITMAPINFOHEADER:
|
|
316 sh->bih = malloc(sizeof(BITMAPINFOHEADER));
|
|
317 memset(sh->bih, 0, sizeof(BITMAPINFOHEADER));
|
|
318 sh->bih->biSize = 40;
|
|
319 sh->disp_w = sh->bih->biWidth = stream_read_word(demuxer->stream);
|
|
320 sh->disp_h = sh->bih->biHeight = stream_read_word(demuxer->stream);
|
|
321 sh->bih->biPlanes = 1;
|
|
322 sh->bih->biBitCount = 24;
|
|
323 sh->bih->biCompression = sh->format;
|
|
324 sh->bih->biSizeImage= sh->bih->biWidth*sh->bih->biHeight*3;
|
|
325
|
|
326 sh->fps = stream_read_word(demuxer->stream);
|
|
327 sh->frametime = 1.0f/sh->fps;
|
|
328
|
|
329 stream_skip(demuxer->stream, 4);
|
|
330 stream_skip(demuxer->stream, 2);
|
|
331 stream_skip(demuxer->stream, 4);
|
|
332 stream_skip(demuxer->stream, 2);
|
|
333
|
|
334 /* h263 hack */
|
|
335 v = stream_read_dword(demuxer->stream);
|
|
336 switch (v)
|
|
337 {
|
|
338 case 0x10000000:
|
|
339 /* sub id: 0 */
|
|
340 /* codec id: rv10 */
|
|
341 break;
|
|
342 case 0x10003000:
|
|
343 case 0x10003001:
|
|
344 /* sub id: 3 */
|
|
345 /* codec id: rv10 */
|
|
346 break;
|
|
347 case 0x20001000:
|
|
348 /* codec id: rv20 */
|
|
349 break;
|
|
350 default:
|
|
351 /* codec id: none */
|
|
352 }
|
|
353
|
|
354 /* insert as stream */
|
|
355 demuxer->video->sh = sh;
|
|
356 sh->ds = demuxer->video;
|
|
357 demuxer->video->id = stream_id;
|
|
358 if (priv->last_v_stream+1 < MAX_STREAMS)
|
|
359 {
|
|
360 priv->v_streams[priv->last_v_stream] = stream_id;
|
|
361 priv->last_v_stream++;
|
|
362 }
|
|
363 }
|
|
364
|
|
365 skip_this_chunk:
|
|
366 /* skip codec info */
|
|
367 v = stream_tell(demuxer->stream) - codec_pos;
|
|
368 stream_skip(demuxer->stream, codec_data_size - v);
|
|
369 break;
|
|
370 }
|
|
371 case MKTAG('D', 'A', 'T', 'A'):
|
|
372 goto header_end;
|
|
373 default:
|
|
374 printf("Unknown chunk: %x\n", chunk);
|
|
375 stream_skip(demuxer->stream, chunk_size - 10);
|
|
376 break;
|
|
377 }
|
|
378 }
|
|
379
|
|
380 header_end:
|
|
381 priv->num_of_packets = stream_read_dword(demuxer->stream);
|
|
382 // stream_skip(demuxer->stream, 4); /* number of packets */
|
|
383 stream_skip(demuxer->stream, 4); /* next data header */
|
|
384
|
|
385 printf("Packets in file: %d\n", priv->num_of_packets);
|
|
386
|
|
387 /* disable seeking */
|
|
388 demuxer->seekable = 0;
|
|
389
|
|
390 fail:
|
|
391 return;
|
|
392 }
|
|
393
|
|
394 void demux_close_real(demuxer_t *demuxer)
|
|
395 {
|
|
396 real_priv_t* priv = demuxer->priv;
|
|
397
|
|
398 if (priv)
|
|
399 free(priv);
|
|
400
|
|
401 return;
|
|
402 }
|