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
|
3788
|
9 /*
|
|
10 Some codecs for Real (from Mike Melanson):
|
|
11
|
|
12 RV10: As has been mentioned, H.263-based; not an unreasonable guess
|
|
13 RV20: RealVideo 2.0, nothing known
|
|
14 RV30: RealVideo 3.0,nothing known, but I don't believe I've ever seen any
|
|
15 media encoded with it
|
|
16 DNET: apparently one of their original audio codecs, to be used with music
|
|
17 SIPR: SiprNet, based on ACELP, and is great for compressing voice
|
|
18 COKR(?): Cooker, the fabled G2 audio codec
|
|
19 */
|
|
20
|
3777
|
21 #include <stdio.h>
|
|
22 #include <stdlib.h>
|
|
23 #include <unistd.h>
|
|
24
|
|
25 #include "config.h"
|
|
26 #include "mp_msg.h"
|
|
27 #include "help_mp.h"
|
|
28
|
|
29 #include "stream.h"
|
|
30 #include "demuxer.h"
|
|
31 #include "stheader.h"
|
|
32 #include "bswap.h"
|
|
33
|
|
34 #define MKTAG(a, b, c, d) (a | (b << 8) | (c << 16) | (d << 24))
|
|
35
|
|
36 #define MAX_STREAMS 10
|
|
37
|
|
38 typedef struct {
|
|
39 int num_of_packets;
|
|
40 int last_a_stream;
|
|
41 int a_streams[MAX_STREAMS];
|
|
42 int last_v_stream;
|
|
43 int v_streams[MAX_STREAMS];
|
|
44 } real_priv_t;
|
|
45
|
|
46 static void get_str(int isbyte, demuxer_t *demuxer, char *buf, int buf_size)
|
|
47 {
|
|
48 int len, i;
|
|
49 char *q;
|
|
50
|
|
51 if (isbyte)
|
|
52 len = stream_read_char(demuxer->stream);
|
|
53 else
|
|
54 len = stream_read_word(demuxer->stream);
|
|
55 #if 1
|
|
56 q = buf;
|
|
57 for (i = 0; i < len; i++)
|
|
58 if (i < (buf_size - 1))
|
|
59 *q++ = stream_read_char(demuxer->stream);
|
|
60 *q = 0;
|
|
61 #else
|
|
62 stream_read(demuxer->stream, buf, buf_size);
|
|
63 #endif
|
|
64 }
|
|
65
|
|
66 static void skip_str(int isbyte, demuxer_t *demuxer)
|
|
67 {
|
|
68 int len;
|
|
69
|
|
70 if (isbyte)
|
|
71 len = stream_read_char(demuxer->stream);
|
|
72 else
|
|
73 len = stream_read_word(demuxer->stream);
|
|
74
|
|
75 stream_skip(demuxer->stream, len);
|
|
76
|
|
77 printf("skip_str: %d bytes skipped\n", len);
|
|
78 }
|
|
79
|
|
80 int real_check_file(demuxer_t* demuxer){
|
|
81 real_priv_t *priv;
|
|
82 int c;
|
|
83
|
|
84 mp_msg(MSGT_DEMUX,MSGL_V,"Checking for REAL\n");
|
|
85
|
|
86 c = stream_read_dword_le(demuxer->stream);
|
|
87 if (c == -256)
|
|
88 return 0; /* EOF */
|
|
89 if (c != MKTAG('.', 'R', 'M', 'F'))
|
|
90 return 0; /* bad magic */
|
|
91
|
|
92 priv = malloc(sizeof(real_priv_t));
|
|
93 memset(priv, 0, sizeof(real_priv_t));
|
|
94 demuxer->priv = priv;
|
|
95
|
|
96 return 1;
|
|
97 }
|
|
98
|
|
99 // return value:
|
|
100 // 0 = EOF or no stream found
|
|
101 // 1 = successfully read a packet
|
|
102 int demux_real_fill_buffer(demuxer_t *demuxer)
|
|
103 {
|
|
104 real_priv_t *priv = demuxer->priv;
|
|
105 demux_stream_t *ds = NULL;
|
|
106 int len;
|
|
107 int timestamp;
|
|
108 int stream_id;
|
|
109 int i;
|
|
110
|
|
111 // printf("num_of_packets: %d\n", priv->num_of_packets);
|
|
112
|
|
113 loop:
|
|
114 if (priv->num_of_packets == 0)
|
|
115 return 0; /* EOF */
|
|
116 stream_skip(demuxer->stream, 2);
|
|
117 len = stream_read_word(demuxer->stream);
|
|
118 if (len == -256) /* EOF */
|
|
119 return 0;
|
|
120 if (len < 12)
|
|
121 {
|
|
122 printf("bad packet len (%d)\n", len);
|
|
123 stream_skip(demuxer->stream, len);
|
|
124 goto loop;
|
|
125 // return 0; /* bad packet */
|
|
126 }
|
|
127 stream_id = stream_read_word(demuxer->stream);
|
|
128 timestamp = stream_read_dword(demuxer->stream);
|
|
129
|
|
130 // printf("packet: len: %d, stream_id: %d, timestamp: %d\n",
|
|
131 // len, stream_id, timestamp);
|
|
132
|
|
133 stream_skip(demuxer->stream, 1); /* reserved */
|
|
134 stream_skip(demuxer->stream, 1); /* flags */
|
|
135
|
|
136 priv->num_of_packets--;
|
|
137 len -= 12;
|
|
138
|
|
139 /* check if stream_id is audio stream */
|
|
140 for (i = 0; i < priv->last_a_stream; i++)
|
|
141 {
|
|
142 if (priv->a_streams[i] == stream_id)
|
|
143 {
|
|
144 // printf("packet is audio (id: %d)\n", stream_id);
|
|
145 ds = demuxer->audio; /* FIXME */
|
|
146 break;
|
|
147 }
|
|
148 }
|
|
149 /* check if stream_id is video stream */
|
|
150 for (i = 0; i < priv->last_v_stream; i++)
|
|
151 {
|
|
152 if (priv->v_streams[i] == stream_id)
|
|
153 {
|
|
154 // printf("packet is video (id: %d)\n", stream_id);
|
|
155 ds = demuxer->video; /* FIXME */
|
|
156 break;
|
|
157 }
|
|
158 }
|
|
159
|
|
160 /* id not found */
|
|
161 if (ds == NULL)
|
|
162 {
|
|
163 printf("unknown stream id (%d)\n", stream_id);
|
|
164 stream_skip(demuxer->stream, len);
|
|
165 goto loop;
|
|
166 }
|
|
167
|
|
168 demuxer->filepos = stream_tell(demuxer->stream);
|
|
169 ds_read_packet(ds, demuxer->stream, len, timestamp/90000.0f, demuxer->filepos, 0);
|
|
170
|
|
171 return 1;
|
|
172 }
|
|
173
|
|
174 void demux_open_real(demuxer_t* demuxer)
|
|
175 {
|
|
176 real_priv_t* priv = demuxer->priv;
|
|
177 int num_of_headers;
|
|
178 int i;
|
|
179
|
|
180 stream_skip(demuxer->stream, 4); /* header size */
|
|
181 stream_skip(demuxer->stream, 2);
|
|
182 stream_skip(demuxer->stream, 4);
|
|
183 num_of_headers = stream_read_dword(demuxer->stream);
|
|
184 // stream_skip(demuxer->stream, 4); /* number of headers */
|
|
185
|
|
186 /* parse chunks */
|
|
187 for (i = 1; i < num_of_headers; i++)
|
|
188 {
|
|
189 int chunk, chunk_size;
|
|
190
|
|
191 chunk = stream_read_dword_le(demuxer->stream);
|
|
192 chunk_size = stream_read_dword(demuxer->stream);
|
|
193
|
|
194 stream_skip(demuxer->stream, 2);
|
|
195
|
|
196 if (chunk_size < 10)
|
|
197 goto fail;
|
|
198
|
|
199 printf("Chunk: %.4s (size: %d)\n",
|
|
200 (char *)&chunk, chunk_size);
|
|
201
|
|
202 switch(chunk)
|
|
203 {
|
|
204 case MKTAG('P', 'R', 'O', 'P'):
|
|
205 printf("Properties chunk\n");
|
|
206 stream_skip(demuxer->stream, 4); /* max bitrate */
|
|
207 stream_skip(demuxer->stream, 4); /* avg bitrate */
|
|
208 stream_skip(demuxer->stream, 4); /* max packet size */
|
|
209 stream_skip(demuxer->stream, 4); /* avg packet size */
|
|
210 stream_skip(demuxer->stream, 4); /* nb packets */
|
|
211 stream_skip(demuxer->stream, 4); /* duration */
|
|
212 stream_skip(demuxer->stream, 4); /* preroll */
|
|
213 stream_skip(demuxer->stream, 4); /* index offset */
|
|
214 stream_skip(demuxer->stream, 4); /* data offset */
|
|
215 stream_skip(demuxer->stream, 2); /* nb streams */
|
|
216 stream_skip(demuxer->stream, 2); /* flags */
|
|
217 break;
|
|
218 case MKTAG('C', 'O', 'N', 'T'):
|
3788
|
219 {
|
|
220 char *buf;
|
|
221 int len;
|
|
222
|
3777
|
223 printf("Broadcasting informations (title, author, copyright, comment)\n");
|
3788
|
224
|
|
225 len = stream_read_word(demuxer->stream);
|
|
226 if (len > 0)
|
|
227 {
|
|
228 buf = malloc(len+1);
|
|
229 stream_read(demuxer->stream, buf, len);
|
|
230 demux_info_add(demuxer, "name", buf);
|
|
231 free(buf);
|
|
232 }
|
|
233
|
|
234 len = stream_read_word(demuxer->stream);
|
|
235 if (len > 0)
|
|
236 {
|
|
237 buf = malloc(len+1);
|
|
238 stream_read(demuxer->stream, buf, len);
|
|
239 demux_info_add(demuxer, "author", buf);
|
|
240 free(buf);
|
|
241 }
|
|
242
|
|
243 len = stream_read_word(demuxer->stream);
|
|
244 if (len > 0)
|
|
245 {
|
|
246 buf = malloc(len+1);
|
|
247 stream_read(demuxer->stream, buf, len);
|
|
248 demux_info_add(demuxer, "copyright", buf);
|
|
249 free(buf);
|
|
250 }
|
|
251
|
|
252 len = stream_read_word(demuxer->stream);
|
|
253 if (len > 0)
|
|
254 {
|
|
255 buf = malloc(len+1);
|
|
256 stream_read(demuxer->stream, buf, len);
|
|
257 demux_info_add(demuxer, "comment", buf);
|
|
258 free(buf);
|
|
259 }
|
|
260
|
|
261 // skip_str(0, demuxer); /* title */
|
|
262 // skip_str(0, demuxer); /* author */
|
|
263 // skip_str(0, demuxer); /* copyright */
|
|
264 // skip_str(0, demuxer); /* comment */
|
3777
|
265 break;
|
3788
|
266 }
|
3777
|
267 case MKTAG('M', 'D', 'P', 'R'):
|
|
268 {
|
|
269 /* new stream */
|
|
270 int stream_id;
|
|
271 int bitrate;
|
|
272 int codec_data_size;
|
|
273 int codec_pos;
|
|
274 int v;
|
|
275
|
|
276 stream_id = stream_read_word(demuxer->stream);
|
|
277 printf("Found new stream (id: %d)\n", stream_id);
|
|
278
|
|
279 stream_skip(demuxer->stream, 4); /* max bitrate */
|
|
280 bitrate = stream_read_dword(demuxer->stream); /* bitrate */
|
|
281 stream_skip(demuxer->stream, 4); /* max packet size */
|
|
282 stream_skip(demuxer->stream, 4); /* avg packet size */
|
|
283 stream_skip(demuxer->stream, 4); /* start time */
|
|
284 stream_skip(demuxer->stream, 4); /* preroll */
|
|
285 stream_skip(demuxer->stream, 4); /* duration */
|
|
286
|
|
287 skip_str(1, demuxer); /* stream description */
|
|
288 skip_str(1, demuxer); /* mimetype */
|
|
289
|
|
290 codec_data_size = stream_read_dword(demuxer->stream);
|
|
291 codec_pos = stream_tell(demuxer->stream);
|
|
292
|
|
293 v = stream_read_dword(demuxer->stream);
|
|
294 if (v == MKTAG(0xfd, 'a', 'r', '.')) /* audio header */
|
|
295 {
|
|
296 sh_audio_t *sh = new_sh_audio(demuxer, stream_id);
|
|
297
|
|
298 printf("Found audio stream!\n");
|
3788
|
299 // printf("pos: %x\n", stream_tell(demuxer->stream));
|
|
300 stream_skip(demuxer->stream, 2); /* version (4 or 5) */
|
|
301 stream_skip(demuxer->stream, 2);
|
|
302 stream_skip(demuxer->stream, 4); /* .ra4 or .ra5 */
|
3777
|
303 stream_skip(demuxer->stream, 4);
|
3788
|
304 stream_skip(demuxer->stream, 2); /* version (4 or 5) */
|
3777
|
305 stream_skip(demuxer->stream, 4); /* header size */
|
|
306 stream_skip(demuxer->stream, 2); /* add codec info */
|
|
307 stream_skip(demuxer->stream, 4); /* coded frame size */
|
|
308 stream_skip(demuxer->stream, 4);
|
|
309 stream_skip(demuxer->stream, 4);
|
|
310 stream_skip(demuxer->stream, 4);
|
|
311 stream_skip(demuxer->stream, 2); /* 1 */
|
|
312 stream_skip(demuxer->stream, 2); /* coded frame size */
|
|
313 stream_skip(demuxer->stream, 4);
|
|
314
|
|
315 sh->samplerate = stream_read_word(demuxer->stream);
|
|
316 stream_skip(demuxer->stream, 4);
|
|
317 sh->channels = stream_read_word(demuxer->stream);
|
|
318
|
|
319 {
|
|
320 char buf[128];
|
|
321
|
3788
|
322 skip_str(1, demuxer);
|
|
323 // get_str(1, demuxer, buf, sizeof(buf));
|
3777
|
324 get_str(1, demuxer, buf, sizeof(buf));
|
|
325
|
|
326 v = 0; /* not supported audio codec */
|
3788
|
327 if (strstr(buf, "dnet"))
|
3777
|
328 {
|
|
329 printf("Found AC3 audio\n");
|
|
330 sh->format = 0x2000; /* ac3 */
|
|
331 v = 1;
|
|
332 }
|
3788
|
333 if (strstr(buf, "sipr"))
|
|
334 {
|
|
335 printf("Found SiproLab's ACELP\n");
|
|
336 sh->format = 0x130;
|
|
337 v = 1;
|
|
338 }
|
|
339 if (strstr(buf, "cook"))
|
|
340 {
|
|
341 printf("Found Real's GeneralCooker (unsupported)\n");
|
|
342 v = 0;
|
|
343 }
|
3777
|
344 }
|
|
345 // skip_str(1, demuxer); /* desc */
|
|
346 // skip_str(1, demuxer); /* desc */
|
|
347
|
|
348 /* the audio codec name is stored in desc */
|
|
349 /* available codecs: DNET -> ac3 */
|
|
350 // sh->format = 0x2000; /* ac3 */
|
|
351
|
|
352 if (v)
|
|
353 {
|
3788
|
354
|
|
355 /* Emulate WAVEFORMATEX struct: */
|
|
356 sh->wf = malloc(sizeof(WAVEFORMATEX));
|
|
357 memset(sh->wf, 0, sizeof(WAVEFORMATEX));
|
|
358 sh->wf->wFormatTag = sh->format;
|
|
359 sh->wf->nChannels = sh->channels;
|
|
360 sh->wf->wBitsPerSample = 16;
|
|
361 sh->wf->nSamplesPerSec = sh->samplerate;
|
|
362 sh->wf->nAvgBytesPerSec = bitrate;
|
|
363 sh->wf->nBlockAlign = 19; /* 19 for acelp, pff */
|
|
364 sh->wf->cbSize = 0;
|
|
365
|
3777
|
366 /* insert as stream */
|
|
367 demuxer->audio->sh = sh;
|
|
368 sh->ds = demuxer->audio;
|
|
369 demuxer->audio->id = stream_id;
|
|
370
|
|
371 if (priv->last_a_stream+1 < MAX_STREAMS)
|
|
372 {
|
|
373 priv->a_streams[priv->last_a_stream] = stream_id;
|
|
374 priv->last_a_stream++;
|
|
375 }
|
|
376 }
|
|
377 }
|
|
378 else
|
|
379 {
|
|
380 /* video header */
|
|
381 sh_video_t *sh = new_sh_video(demuxer, stream_id);
|
|
382
|
|
383 v = stream_read_dword_le(demuxer->stream);
|
|
384 printf("video: %.4s (%x)\n", (char *)&v, v);
|
|
385 if (v != MKTAG('V', 'I', 'D', 'O'))
|
|
386 {
|
|
387 mp_msg(MSGT_DEMUX, MSGL_ERR, "Unsupported video codec\n");
|
|
388 goto skip_this_chunk;
|
|
389 }
|
|
390
|
|
391 sh->format = stream_read_dword_le(demuxer->stream); /* fourcc */
|
|
392 printf("video fourcc: %.4s (%x)\n", (char *)&sh->format, sh->format);
|
|
393
|
|
394 // emulate BITMAPINFOHEADER:
|
|
395 sh->bih = malloc(sizeof(BITMAPINFOHEADER));
|
|
396 memset(sh->bih, 0, sizeof(BITMAPINFOHEADER));
|
|
397 sh->bih->biSize = 40;
|
|
398 sh->disp_w = sh->bih->biWidth = stream_read_word(demuxer->stream);
|
|
399 sh->disp_h = sh->bih->biHeight = stream_read_word(demuxer->stream);
|
|
400 sh->bih->biPlanes = 1;
|
|
401 sh->bih->biBitCount = 24;
|
|
402 sh->bih->biCompression = sh->format;
|
|
403 sh->bih->biSizeImage= sh->bih->biWidth*sh->bih->biHeight*3;
|
|
404
|
|
405 sh->fps = stream_read_word(demuxer->stream);
|
|
406 sh->frametime = 1.0f/sh->fps;
|
|
407
|
|
408 stream_skip(demuxer->stream, 4);
|
|
409 stream_skip(demuxer->stream, 2);
|
|
410 stream_skip(demuxer->stream, 4);
|
|
411 stream_skip(demuxer->stream, 2);
|
|
412
|
|
413 /* h263 hack */
|
|
414 v = stream_read_dword(demuxer->stream);
|
|
415 switch (v)
|
|
416 {
|
|
417 case 0x10000000:
|
|
418 /* sub id: 0 */
|
|
419 /* codec id: rv10 */
|
|
420 break;
|
|
421 case 0x10003000:
|
|
422 case 0x10003001:
|
|
423 /* sub id: 3 */
|
|
424 /* codec id: rv10 */
|
|
425 break;
|
|
426 case 0x20001000:
|
3788
|
427 case 0x20100001:
|
3777
|
428 /* codec id: rv20 */
|
|
429 break;
|
|
430 default:
|
|
431 /* codec id: none */
|
3788
|
432 printf("unknown id: %x\n", v);
|
3777
|
433 }
|
|
434
|
|
435 /* insert as stream */
|
|
436 demuxer->video->sh = sh;
|
|
437 sh->ds = demuxer->video;
|
|
438 demuxer->video->id = stream_id;
|
|
439 if (priv->last_v_stream+1 < MAX_STREAMS)
|
|
440 {
|
|
441 priv->v_streams[priv->last_v_stream] = stream_id;
|
|
442 priv->last_v_stream++;
|
|
443 }
|
|
444 }
|
|
445
|
|
446 skip_this_chunk:
|
|
447 /* skip codec info */
|
|
448 v = stream_tell(demuxer->stream) - codec_pos;
|
|
449 stream_skip(demuxer->stream, codec_data_size - v);
|
|
450 break;
|
|
451 }
|
|
452 case MKTAG('D', 'A', 'T', 'A'):
|
|
453 goto header_end;
|
|
454 default:
|
|
455 printf("Unknown chunk: %x\n", chunk);
|
|
456 stream_skip(demuxer->stream, chunk_size - 10);
|
|
457 break;
|
|
458 }
|
|
459 }
|
|
460
|
|
461 header_end:
|
|
462 priv->num_of_packets = stream_read_dword(demuxer->stream);
|
|
463 // stream_skip(demuxer->stream, 4); /* number of packets */
|
|
464 stream_skip(demuxer->stream, 4); /* next data header */
|
|
465
|
|
466 printf("Packets in file: %d\n", priv->num_of_packets);
|
|
467
|
|
468 /* disable seeking */
|
|
469 demuxer->seekable = 0;
|
|
470
|
|
471 fail:
|
|
472 return;
|
|
473 }
|
|
474
|
|
475 void demux_close_real(demuxer_t *demuxer)
|
|
476 {
|
|
477 real_priv_t* priv = demuxer->priv;
|
|
478
|
|
479 if (priv)
|
|
480 free(priv);
|
|
481
|
|
482 return;
|
|
483 }
|