blob: 3f096e333eab05f8d6856849821c686afa51338b [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20/**
21 * Default file player/writer buffer size.
22 */
23#include <pjmedia/avi_stream.h>
24#include <pjmedia/avi.h>
25#include <pjmedia/errno.h>
26#include <pjmedia/wave.h>
27#include <pj/assert.h>
28#include <pj/file_access.h>
29#include <pj/file_io.h>
30#include <pj/log.h>
31#include <pj/pool.h>
32#include <pj/string.h>
33
34
35#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
36
37
38#define THIS_FILE "avi_player.c"
39
40#define AVIF_MUSTUSEINDEX 0x00000020
41#define AVIF_ISINTERLEAVED 0x00000100
42#define AVISF_DISABLED 0x00000001
43#define AVISF_VIDEO_PALCHANGES 0x00010000
44
45#define AVI_EOF 0xFFEEFFEE
46
47#define COMPARE_TAG(doc_tag, tag) (doc_tag == *((pj_uint32_t *)avi_tags[tag]))
48
49#define SIGNATURE PJMEDIA_SIG_PORT_VID_AVI_PLAYER
50
51#define VIDEO_CLOCK_RATE 90000
52
53#if 0
54# define TRACE_(x) PJ_LOG(4,x)
55#else
56# define TRACE_(x)
57#endif
58
59#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
60 static void data_to_host(void *data, pj_uint8_t bits, unsigned count)
61 {
62 unsigned i;
63
64 count /= (bits == 32? 4 : 2);
65
66 if (bits == 32) {
67 pj_int32_t *data32 = (pj_int32_t *)data;
68 for (i=0; i<count; ++i)
69 data32[i] = pj_swap32(data32[i]);
70 } else {
71 pj_int16_t *data16 = (pj_int16_t *)data;
72 for (i=0; i<count; ++i)
73 data16[i] = pj_swap16(data16[i]);
74 }
75
76 }
77 static void data_to_host2(void *data, pj_uint8_t nsizes,
78 pj_uint8_t *sizes)
79 {
80 unsigned i;
81 pj_int8_t *datap = (pj_int8_t *)data;
82 for (i = 0; i < nsizes; i++) {
83 data_to_host(datap, 32, sizes[i]);
84 datap += sizes[i++];
85 if (i >= nsizes)
86 break;
87 data_to_host(datap, 16, sizes[i]);
88 datap += sizes[i];
89 }
90 }
91#else
92# define data_to_host(data, bits, count)
93# define data_to_host2(data, nsizes, sizes)
94#endif
95
96typedef struct avi_fmt_info
97{
98 pjmedia_format_id fmt_id;
99 pjmedia_format_id eff_fmt_id;
100} avi_fmt_info;
101
102static avi_fmt_info avi_fmts[] =
103{
104 {PJMEDIA_FORMAT_MJPEG}, {PJMEDIA_FORMAT_H264},
105 {PJMEDIA_FORMAT_UYVY}, {PJMEDIA_FORMAT_YUY2},
106 {PJMEDIA_FORMAT_IYUV}, {PJMEDIA_FORMAT_I420},
107 {PJMEDIA_FORMAT_DIB}, {PJMEDIA_FORMAT_RGB24},
108 {PJMEDIA_FORMAT_RGB32},
109 {PJMEDIA_FORMAT_PACK('X','V','I','D'), PJMEDIA_FORMAT_MPEG4},
110 {PJMEDIA_FORMAT_PACK('x','v','i','d'), PJMEDIA_FORMAT_MPEG4},
111 {PJMEDIA_FORMAT_PACK('D','I','V','X'), PJMEDIA_FORMAT_MPEG4},
112 {PJMEDIA_FORMAT_PACK('F','M','P','4'), PJMEDIA_FORMAT_MPEG4},
113 {PJMEDIA_FORMAT_PACK('D','X','5','0'), PJMEDIA_FORMAT_MPEG4}
114};
115
116struct pjmedia_avi_streams
117{
118 unsigned num_streams;
119 pjmedia_port **streams;
120};
121
122struct avi_reader_port
123{
124 pjmedia_port base;
125 unsigned stream_id;
126 unsigned options;
127 pjmedia_format_id fmt_id;
128 unsigned usec_per_frame;
129 pj_uint16_t bits_per_sample;
130 pj_bool_t eof;
131 pj_off_t fsize;
132 pj_off_t start_data;
133 pj_uint8_t pad;
134 pj_oshandle_t fd;
135 pj_ssize_t size_left;
136 pj_timestamp next_ts;
137
138 pj_status_t (*cb)(pjmedia_port*, void*);
139};
140
141
142static pj_status_t avi_get_frame(pjmedia_port *this_port,
143 pjmedia_frame *frame);
144static pj_status_t avi_on_destroy(pjmedia_port *this_port);
145
146static struct avi_reader_port *create_avi_port(pj_pool_t *pool)
147{
148 const pj_str_t name = pj_str("file");
149 struct avi_reader_port *port;
150
151 port = PJ_POOL_ZALLOC_T(pool, struct avi_reader_port);
152 if (!port)
153 return NULL;
154
155 /* Put in default values.
156 * These will be overriden once the file is read.
157 */
158 pjmedia_port_info_init(&port->base.info, &name, SIGNATURE,
159 8000, 1, 16, 80);
160
161 port->fd = (pj_oshandle_t)(pj_ssize_t)-1;
162 port->base.get_frame = &avi_get_frame;
163 port->base.on_destroy = &avi_on_destroy;
164
165 return port;
166}
167
168#define file_read(fd, data, size) file_read2(fd, data, size, 32)
169#define file_read2(fd, data, size, bits) file_read3(fd, data, size, bits, NULL)
170
171static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size,
172 pj_uint16_t bits, pj_ssize_t *psz_read)
173{
174 pj_ssize_t size_read = size, size_to_read = size;
175 pj_status_t status = pj_file_read(fd, data, &size_read);
176 if (status != PJ_SUCCESS)
177 return status;
178
179 /* Normalize AVI header fields values from little-endian to host
180 * byte order.
181 */
182 if (bits > 0)
183 data_to_host(data, bits, size_read);
184
185 if (size_read != size_to_read) {
186 if (psz_read)
187 *psz_read = size_read;
188 return AVI_EOF;
189 }
190
191 return status;
192}
193
194/*
195 * Create AVI player port.
196 */
197PJ_DEF(pj_status_t)
198pjmedia_avi_player_create_streams(pj_pool_t *pool,
199 const char *filename,
200 unsigned options,
201 pjmedia_avi_streams **p_streams)
202{
203 pjmedia_avi_hdr avi_hdr;
204 struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS];
205 pj_off_t pos;
206 unsigned i, nstr = 0;
207 pj_status_t status = PJ_SUCCESS;
208
209 /* Check arguments. */
210 PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL);
211
212 /* Check the file really exists. */
213 if (!pj_file_exists(filename)) {
214 return PJ_ENOTFOUND;
215 }
216
217 /* Create fport instance. */
218 fport[0] = create_avi_port(pool);
219 if (!fport[0]) {
220 return PJ_ENOMEM;
221 }
222
223 /* Get the file size. */
224 fport[0]->fsize = pj_file_size(filename);
225
226 /* Size must be more than AVI header size */
227 if (fport[0]->fsize <= sizeof(riff_hdr_t) + sizeof(avih_hdr_t) +
228 sizeof(strl_hdr_t))
229 {
230 return PJMEDIA_EINVALIMEDIATYPE;
231 }
232
233 /* Open file. */
234 status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[0]->fd);
235 if (status != PJ_SUCCESS)
236 return status;
237
238 /* Read the RIFF + AVIH header. */
239 status = file_read(fport[0]->fd, &avi_hdr,
240 sizeof(riff_hdr_t) + sizeof(avih_hdr_t));
241 if (status != PJ_SUCCESS)
242 goto on_error;
243
244 /* Validate AVI file. */
245 if (!COMPARE_TAG(avi_hdr.riff_hdr.riff, PJMEDIA_AVI_RIFF_TAG) ||
246 !COMPARE_TAG(avi_hdr.riff_hdr.avi, PJMEDIA_AVI_AVI_TAG) ||
247 !COMPARE_TAG(avi_hdr.avih_hdr.list_tag, PJMEDIA_AVI_LIST_TAG) ||
248 !COMPARE_TAG(avi_hdr.avih_hdr.hdrl_tag, PJMEDIA_AVI_HDRL_TAG) ||
249 !COMPARE_TAG(avi_hdr.avih_hdr.avih, PJMEDIA_AVI_AVIH_TAG))
250 {
251 status = PJMEDIA_EINVALIMEDIATYPE;
252 goto on_error;
253 }
254
255 PJ_LOG(5, (THIS_FILE, "The AVI file has %d streams.",
256 avi_hdr.avih_hdr.num_streams));
257
258 /* Unsupported AVI format. */
259 if (avi_hdr.avih_hdr.num_streams > PJMEDIA_AVI_MAX_NUM_STREAMS) {
260 status = PJMEDIA_EAVIUNSUPP;
261 goto on_error;
262 }
263
264 /**
265 * TODO: Possibly unsupported AVI format.
266 * If you encounter this warning, verify whether the avi player
267 * is working properly.
268 */
269 if (avi_hdr.avih_hdr.flags & AVIF_MUSTUSEINDEX ||
270 avi_hdr.avih_hdr.pad > 1)
271 {
272 PJ_LOG(3, (THIS_FILE, "Warning!!! Possibly unsupported AVI format: "
273 "flags:%d, pad:%d", avi_hdr.avih_hdr.flags,
274 avi_hdr.avih_hdr.pad));
275 }
276
277 /* Read the headers of each stream. */
278 for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
279 pj_size_t elem = 0;
280 pj_ssize_t size_to_read;
281
282 /* Read strl header */
283 status = file_read(fport[0]->fd, &avi_hdr.strl_hdr[i],
284 sizeof(strl_hdr_t));
285 if (status != PJ_SUCCESS)
286 goto on_error;
287
288 elem = COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
289 PJMEDIA_AVI_VIDS_TAG) ?
290 sizeof(strf_video_hdr_t) :
291 COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
292 PJMEDIA_AVI_AUDS_TAG) ?
293 sizeof(strf_audio_hdr_t) : 0;
294
295 /* Read strf header */
296 status = file_read2(fport[0]->fd, &avi_hdr.strf_hdr[i],
297 elem, 0);
298 if (status != PJ_SUCCESS)
299 goto on_error;
300
301 /* Normalize the endian */
302 if (elem == sizeof(strf_video_hdr_t))
303 data_to_host2(&avi_hdr.strf_hdr[i],
304 sizeof(strf_video_hdr_sizes)/
305 sizeof(strf_video_hdr_sizes[0]),
306 strf_video_hdr_sizes);
307 else if (elem == sizeof(strf_audio_hdr_t))
308 data_to_host2(&avi_hdr.strf_hdr[i],
309 sizeof(strf_audio_hdr_sizes)/
310 sizeof(strf_audio_hdr_sizes[0]),
311 strf_audio_hdr_sizes);
312
313 /* Skip the remainder of the header */
314 size_to_read = avi_hdr.strl_hdr[i].list_sz - (sizeof(strl_hdr_t) -
315 8) - elem;
316 status = pj_file_setpos(fport[0]->fd, size_to_read, PJ_SEEK_CUR);
317 if (status != PJ_SUCCESS) {
318 goto on_error;
319 }
320 }
321
322 /* Finish reading the AVIH header */
323 status = pj_file_setpos(fport[0]->fd, avi_hdr.avih_hdr.list_sz +
324 sizeof(riff_hdr_t) + 8, PJ_SEEK_SET);
325 if (status != PJ_SUCCESS) {
326 goto on_error;
327 }
328
329 /* Skip any JUNK or LIST INFO until we get MOVI tag */
330 do {
331 pjmedia_avi_subchunk ch;
332 int read = 0;
333
334 status = file_read(fport[0]->fd, &ch, sizeof(pjmedia_avi_subchunk));
335 if (status != PJ_SUCCESS) {
336 goto on_error;
337 }
338
339 if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
340 {
341 read = 4;
342 status = file_read(fport[0]->fd, &ch, read);
343 if (COMPARE_TAG(ch.id, PJMEDIA_AVI_MOVI_TAG))
344 break;
345 }
346
347 status = pj_file_setpos(fport[0]->fd, ch.len-read, PJ_SEEK_CUR);
348 if (status != PJ_SUCCESS) {
349 goto on_error;
350 }
351 } while(1);
352
353 status = pj_file_getpos(fport[0]->fd, &pos);
354 if (status != PJ_SUCCESS)
355 goto on_error;
356
357 for (i = 0, nstr = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
358 pjmedia_format_id fmt_id;
359
360 /* Skip non-audio, non-video, or disabled streams) */
361 if ((!COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
362 PJMEDIA_AVI_VIDS_TAG) &&
363 !COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
364 PJMEDIA_AVI_AUDS_TAG)) ||
365 avi_hdr.strl_hdr[i].flags & AVISF_DISABLED)
366 {
367 continue;
368 }
369
370 if (COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
371 PJMEDIA_AVI_VIDS_TAG))
372 {
373 int j;
374
375 if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES) {
376 PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
377 continue;
378 }
379
380 fmt_id = avi_hdr.strl_hdr[i].codec;
381 for (j = sizeof(avi_fmts)/sizeof(avi_fmts[0])-1; j >= 0; j--) {
382 /* Check supported video formats here */
383 if (fmt_id == avi_fmts[j].fmt_id) {
384 if (avi_fmts[j].eff_fmt_id)
385 fmt_id = avi_fmts[j].eff_fmt_id;
386 break;
387 }
388 }
389
390 if (j < 0) {
391 PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
392 continue;
393 }
394 } else {
395 /* Check supported audio formats here */
396 if ((avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_PCM &&
397 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ALAW &&
398 avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ULAW &&
399 avi_hdr.strl_hdr[i].codec != PJMEDIA_WAVE_FMT_TAG_PCM) ||
400 avi_hdr.strf_hdr[i].strf_audio_hdr.bits_per_sample != 16)
401 {
402 PJ_LOG(4, (THIS_FILE, "Unsupported audio stream"));
403 continue;
404 }
405 /* Normalize format ID */
406 fmt_id = avi_hdr.strl_hdr[i].codec;
407 if (avi_hdr.strl_hdr[i].codec == PJMEDIA_WAVE_FMT_TAG_PCM)
408 fmt_id = PJMEDIA_FORMAT_PCM;
409 }
410
411 if (nstr > 0) {
412 /* Create fport instance. */
413 fport[nstr] = create_avi_port(pool);
414 if (!fport[nstr]) {
415 status = PJ_ENOMEM;
416 goto on_error;
417 }
418
419 /* Open file. */
420 status = pj_file_open(pool, filename, PJ_O_RDONLY,
421 &fport[nstr]->fd);
422 if (status != PJ_SUCCESS)
423 goto on_error;
424
425 /* Set the file position */
426 status = pj_file_setpos(fport[nstr]->fd, pos, PJ_SEEK_SET);
427 if (status != PJ_SUCCESS) {
428 goto on_error;
429 }
430 }
431
432 fport[nstr]->stream_id = i;
433 fport[nstr]->fmt_id = fmt_id;
434
435 nstr++;
436 }
437
438 if (nstr == 0) {
439 status = PJMEDIA_EAVIUNSUPP;
440 goto on_error;
441 }
442
443 for (i = 0; i < nstr; i++) {
444 strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id];
445
446 /* Initialize */
447 fport[i]->options = options;
448 fport[i]->fsize = fport[0]->fsize;
449 /* Current file position now points to start of data */
450 fport[i]->start_data = pos;
451
452 if (COMPARE_TAG(strl_hdr->data_type, PJMEDIA_AVI_VIDS_TAG)) {
453 strf_video_hdr_t *strf_hdr =
454 &avi_hdr.strf_hdr[fport[i]->stream_id].strf_video_hdr;
455 const pjmedia_video_format_info *vfi;
456
457 vfi = pjmedia_get_video_format_info(
458 pjmedia_video_format_mgr_instance(),
459 strl_hdr->codec);
460
461 fport[i]->bits_per_sample = (vfi ? vfi->bpp : 0);
462 fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame;
463 pjmedia_format_init_video(&fport[i]->base.info.fmt,
464 fport[i]->fmt_id,
465 strf_hdr->biWidth,
466 strf_hdr->biHeight,
467 strl_hdr->rate,
468 strl_hdr->scale);
469#if 0
470 /* The calculation below is wrong. strf_hdr->biSizeImage shows
471 * uncompressed size. Looks like we need to go the ugly way to
472 * get the bitrage:
473 * http://www.virtualdub.org/blog/pivot/entry.php?id=159
474 */
475 bps = strf_hdr->biSizeImage * 8 * strl_hdr->rate / strl_hdr->scale;
476 if (bps==0) {
477 /* strf_hdr->biSizeImage may be zero for uncompressed RGB */
478 bps = strf_hdr->biWidth * strf_hdr->biHeight *
479 strf_hdr->biBitCount *
480 strl_hdr->rate / strl_hdr->scale;
481 }
482 fport[i]->base.info.fmt.det.vid.avg_bps = bps;
483 fport[i]->base.info.fmt.det.vid.max_bps = bps;
484#endif
485 } else {
486 strf_audio_hdr_t *strf_hdr =
487 &avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr;
488
489 fport[i]->bits_per_sample = strf_hdr->bits_per_sample;
490 fport[i]->usec_per_frame = avi_hdr.avih_hdr.usec_per_frame;
491 pjmedia_format_init_audio(&fport[i]->base.info.fmt,
492 fport[i]->fmt_id,
493 strf_hdr->sample_rate,
494 strf_hdr->nchannels,
495 strf_hdr->bits_per_sample,
496 20000 /* fport[i]->usec_per_frame */,
497 strf_hdr->bytes_per_sec * 8,
498 strf_hdr->bytes_per_sec * 8);
499 }
500
501 pj_strdup2(pool, &fport[i]->base.info.name, filename);
502 }
503
504 /* Done. */
505 *p_streams = pj_pool_alloc(pool, sizeof(pjmedia_avi_streams));
506 (*p_streams)->num_streams = nstr;
507 (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams,
508 sizeof(pjmedia_port *));
509 for (i = 0; i < nstr; i++)
510 (*p_streams)->streams[i] = &fport[i]->base;
511
512 PJ_LOG(4,(THIS_FILE,
513 "AVI file player '%.*s' created with "
514 "%d media ports",
515 (int)fport[0]->base.info.name.slen,
516 fport[0]->base.info.name.ptr,
517 (*p_streams)->num_streams));
518
519 return PJ_SUCCESS;
520
521on_error:
522 fport[0]->base.on_destroy(&fport[0]->base);
523 for (i = 1; i < nstr; i++)
524 fport[i]->base.on_destroy(&fport[i]->base);
525 if (status == AVI_EOF)
526 return PJMEDIA_EINVALIMEDIATYPE;
527 return status;
528}
529
530PJ_DEF(unsigned)
531pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
532{
533 pj_assert(streams);
534 return streams->num_streams;
535}
536
537PJ_DEF(pjmedia_avi_stream *)
538pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
539 unsigned idx)
540{
541 pj_assert(streams);
542 return (idx >=0 && idx < streams->num_streams ?
543 streams->streams[idx] : NULL);
544}
545
546PJ_DEF(pjmedia_avi_stream *)
547pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
548 unsigned start_idx,
549 pjmedia_type media_type)
550{
551 unsigned i;
552
553 pj_assert(streams);
554 for (i = start_idx; i < streams->num_streams; i++)
555 if (streams->streams[i]->info.fmt.type == media_type)
556 return streams->streams[i];
557 return NULL;
558}
559
560
561/*
562 * Get the data length, in bytes.
563 */
564PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream)
565{
566 struct avi_reader_port *fport;
567
568 /* Sanity check */
569 PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
570
571 /* Check that this is really a player port */
572 PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
573
574 fport = (struct avi_reader_port*) stream;
575
576 return (pj_ssize_t)(fport->fsize - fport->start_data);
577}
578
579
580/*
581 * Register a callback to be called when the file reading has reached the
582 * end of file.
583 */
584PJ_DEF(pj_status_t)
585pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
586 void *user_data,
587 pj_status_t (*cb)(pjmedia_avi_stream *stream,
588 void *usr_data))
589{
590 struct avi_reader_port *fport;
591
592 /* Sanity check */
593 PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
594
595 /* Check that this is really a player port */
596 PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
597
598 fport = (struct avi_reader_port*) stream;
599
600 fport->base.port_data.pdata = user_data;
601 fport->cb = cb;
602
603 return PJ_SUCCESS;
604}
605
606
607/*
608 * Get frame from file.
609 */
610static pj_status_t avi_get_frame(pjmedia_port *this_port,
611 pjmedia_frame *frame)
612{
613 struct avi_reader_port *fport = (struct avi_reader_port*)this_port;
614 pj_status_t status;
615 pj_ssize_t size_read = 0, size_to_read = 0;
616
617 pj_assert(fport->base.info.signature == SIGNATURE);
618
619 /* We encountered end of file */
620 if (fport->eof) {
621 pj_status_t status = PJ_SUCCESS;
622
623 PJ_LOG(5,(THIS_FILE, "File port %.*s EOF",
624 (int)fport->base.info.name.slen,
625 fport->base.info.name.ptr));
626
627 /* Call callback, if any */
628 if (fport->cb)
629 status = (*fport->cb)(this_port, fport->base.port_data.pdata);
630
631 /* If callback returns non PJ_SUCCESS or 'no loop' is specified,
632 * return immediately (and don't try to access player port since
633 * it might have been destroyed by the callback).
634 */
635 if ((status != PJ_SUCCESS) ||
636 (fport->options & PJMEDIA_AVI_FILE_NO_LOOP))
637 {
638 frame->type = PJMEDIA_FRAME_TYPE_NONE;
639 frame->size = 0;
640 return PJ_EEOF;
641 }
642
643 /* Rewind file */
644 PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..",
645 (int)fport->base.info.name.slen,
646 fport->base.info.name.ptr));
647 fport->eof = PJ_FALSE;
648 pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET);
649 }
650
651 /* Fill frame buffer. */
652 size_to_read = frame->size;
653 do {
654 pjmedia_avi_subchunk ch = {0, 0};
655 char *cid;
656 unsigned stream_id;
657
658 /* We need to read data from the file past the chunk boundary */
659 if (fport->size_left > 0 && fport->size_left < size_to_read) {
660 status = file_read3(fport->fd, frame->buf, fport->size_left,
661 fport->bits_per_sample, &size_read);
662 if (status != PJ_SUCCESS)
663 goto on_error2;
664 size_to_read -= fport->size_left;
665 fport->size_left = 0;
666 }
667
668 /* Read new chunk data */
669 if (fport->size_left == 0) {
670 pj_off_t pos;
671 pj_file_getpos(fport->fd, &pos);
672
673 /* Data is padded to the nearest WORD boundary */
674 if (fport->pad) {
675 status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR);
676 fport->pad = 0;
677 }
678
679 status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk));
680 if (status != PJ_SUCCESS) {
681 size_read = 0;
682 goto on_error2;
683 }
684
685 cid = (char *)&ch.id;
686 if (cid[0] >= '0' && cid[0] <= '9' &&
687 cid[1] >= '0' && cid[1] <= '9')
688 {
689 stream_id = (cid[0] - '0') * 10 + (cid[1] - '0');
690 } else
691 stream_id = 100;
692 fport->pad = (pj_uint8_t)ch.len & 1;
693
694 TRACE_((THIS_FILE, "Reading movi data at pos %u (%x), id: %.*s, "
695 "length: %u", (unsigned long)pos,
696 (unsigned long)pos, 4, cid, ch.len));
697
698 /* We are only interested in data with our stream id */
699 if (stream_id != fport->stream_id) {
700 if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
701 PJ_LOG(5, (THIS_FILE, "Unsupported LIST tag found in "
702 "the movi data."));
703 else if (COMPARE_TAG(ch.id, PJMEDIA_AVI_RIFF_TAG)) {
704 PJ_LOG(3, (THIS_FILE, "Unsupported format: multiple "
705 "AVIs in a single file."));
706 status = AVI_EOF;
707 goto on_error2;
708 }
709
710 status = pj_file_setpos(fport->fd, ch.len,
711 PJ_SEEK_CUR);
712 continue;
713 }
714 fport->size_left = ch.len;
715 }
716
717 frame->type = (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO ?
718 PJMEDIA_FRAME_TYPE_VIDEO : PJMEDIA_FRAME_TYPE_AUDIO);
719
720 if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
721 if (size_to_read > fport->size_left)
722 size_to_read = fport->size_left;
723 status = file_read3(fport->fd, (char *)frame->buf + frame->size -
724 size_to_read, size_to_read,
725 fport->bits_per_sample, &size_read);
726 if (status != PJ_SUCCESS)
727 goto on_error2;
728 fport->size_left -= size_to_read;
729 } else {
730 pj_assert(frame->size >= ch.len);
731 status = file_read3(fport->fd, frame->buf, ch.len,
732 0, &size_read);
733 if (status != PJ_SUCCESS)
734 goto on_error2;
735 frame->size = ch.len;
736 fport->size_left = 0;
737 }
738
739 break;
740
741 } while(1);
742
743 frame->timestamp.u64 = fport->next_ts.u64;
744 if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
745 if (fport->usec_per_frame) {
746 fport->next_ts.u64 += (fport->usec_per_frame *
747 fport->base.info.fmt.det.aud.clock_rate /
748 1000000);
749 } else {
750 fport->next_ts.u64 += (frame->size *
751 fport->base.info.fmt.det.aud.clock_rate /
752 (fport->base.info.fmt.det.aud.avg_bps / 8));
753 }
754 } else {
755 if (fport->usec_per_frame) {
756 fport->next_ts.u64 += (fport->usec_per_frame * VIDEO_CLOCK_RATE /
757 1000000);
758 } else {
759 fport->next_ts.u64 += (frame->size * VIDEO_CLOCK_RATE /
760 (fport->base.info.fmt.det.vid.avg_bps / 8));
761 }
762 }
763
764 return PJ_SUCCESS;
765
766on_error2:
767 if (status == AVI_EOF) {
768 size_to_read -= size_read;
769 pj_bzero((char *)frame->buf + frame->size - size_to_read,
770 size_to_read);
771 fport->eof = PJ_TRUE;
772
773 return PJ_SUCCESS;
774 }
775
776 return status;
777}
778
779/*
780 * Destroy port.
781 */
782static pj_status_t avi_on_destroy(pjmedia_port *this_port)
783{
784 struct avi_reader_port *fport = (struct avi_reader_port*) this_port;
785
786 pj_assert(this_port->info.signature == SIGNATURE);
787
788 if (fport->fd != (pj_oshandle_t) (pj_ssize_t)-1)
789 pj_file_close(fport->fd);
790 return PJ_SUCCESS;
791}
792
793
794#endif /* PJMEDIA_HAS_VIDEO */