Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjmedia / src / pjmedia / avi_player.c
1 /* $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
96 typedef struct avi_fmt_info
97 {
98     pjmedia_format_id   fmt_id;
99     pjmedia_format_id   eff_fmt_id;
100 } avi_fmt_info;
101
102 static 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
116 struct pjmedia_avi_streams
117 {
118     unsigned        num_streams;
119     pjmedia_port  **streams;
120 };
121
122 struct 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
142 static pj_status_t avi_get_frame(pjmedia_port *this_port, 
143                                  pjmedia_frame *frame);
144 static pj_status_t avi_on_destroy(pjmedia_port *this_port);
145
146 static 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)-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
171 static 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  */
197 PJ_DEF(pj_status_t)
198 pjmedia_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
521 on_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
530 PJ_DEF(unsigned)
531 pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
532 {
533     pj_assert(streams);
534     return streams->num_streams;
535 }
536
537 PJ_DEF(pjmedia_avi_stream *)
538 pjmedia_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
546 PJ_DEF(pjmedia_avi_stream *)
547 pjmedia_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  */
564 PJ_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  */
584 PJ_DEF(pj_status_t)
585 pjmedia_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  */
610 static 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
766 on_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  */
782 static 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) -1)
789         pj_file_close(fport->fd);
790     return PJ_SUCCESS;
791 }
792
793
794 #endif /* PJMEDIA_HAS_VIDEO */