1
/* $Id: avi_player.c 4057 2012-04-17 06:54:50Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
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.
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.
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
21
* Default file player/writer buffer size.
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>
32
#include <pj/string.h>
35
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
38
#define THIS_FILE "avi_player.c"
40
#define AVIF_MUSTUSEINDEX 0x00000020
41
#define AVIF_ISINTERLEAVED 0x00000100
42
#define AVISF_DISABLED 0x00000001
43
#define AVISF_VIDEO_PALCHANGES 0x00010000
45
#define AVI_EOF 0xFFEEFFEE
47
#define COMPARE_TAG(doc_tag, tag) (doc_tag == *((pj_uint32_t *)avi_tags[tag]))
49
#define SIGNATURE PJMEDIA_SIG_PORT_VID_AVI_PLAYER
51
#define VIDEO_CLOCK_RATE 90000
54
# define TRACE_(x) PJ_LOG(4,x)
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)
64
count /= (bits == 32? 4 : 2);
67
pj_int32_t *data32 = (pj_int32_t *)data;
68
for (i=0; i<count; ++i)
69
data32[i] = pj_swap32(data32[i]);
71
pj_int16_t *data16 = (pj_int16_t *)data;
72
for (i=0; i<count; ++i)
73
data16[i] = pj_swap16(data16[i]);
77
static void data_to_host2(void *data, pj_uint8_t nsizes,
81
pj_int8_t *datap = (pj_int8_t *)data;
82
for (i = 0; i < nsizes; i++) {
83
data_to_host(datap, 32, sizes[i]);
87
data_to_host(datap, 16, sizes[i]);
92
# define data_to_host(data, bits, count)
93
# define data_to_host2(data, nsizes, sizes)
96
typedef struct avi_fmt_info
98
pjmedia_format_id fmt_id;
99
pjmedia_format_id eff_fmt_id;
102
static avi_fmt_info avi_fmts[] =
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}
116
struct pjmedia_avi_streams
118
unsigned num_streams;
119
pjmedia_port **streams;
122
struct avi_reader_port
127
pjmedia_format_id fmt_id;
128
unsigned usec_per_frame;
129
pj_uint16_t bits_per_sample;
135
pj_ssize_t size_left;
136
pj_timestamp next_ts;
138
pj_status_t (*cb)(pjmedia_port*, void*);
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);
146
static struct avi_reader_port *create_avi_port(pj_pool_t *pool)
148
const pj_str_t name = pj_str("file");
149
struct avi_reader_port *port;
151
port = PJ_POOL_ZALLOC_T(pool, struct avi_reader_port);
155
/* Put in default values.
156
* These will be overriden once the file is read.
158
pjmedia_port_info_init(&port->base.info, &name, SIGNATURE,
161
port->fd = (pj_oshandle_t)-1;
162
port->base.get_frame = &avi_get_frame;
163
port->base.on_destroy = &avi_on_destroy;
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)
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)
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)
179
/* Normalize AVI header fields values from little-endian to host
183
data_to_host(data, bits, size_read);
185
if (size_read != size_to_read) {
187
*psz_read = size_read;
195
* Create AVI player port.
198
pjmedia_avi_player_create_streams(pj_pool_t *pool,
199
const char *filename,
201
pjmedia_avi_streams **p_streams)
203
pjmedia_avi_hdr avi_hdr;
204
struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS];
206
unsigned i, nstr = 0;
207
pj_status_t status = PJ_SUCCESS;
209
/* Check arguments. */
210
PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL);
212
/* Check the file really exists. */
213
if (!pj_file_exists(filename)) {
217
/* Create fport instance. */
218
fport[0] = create_avi_port(pool);
223
/* Get the file size. */
224
fport[0]->fsize = pj_file_size(filename);
226
/* Size must be more than AVI header size */
227
if (fport[0]->fsize <= sizeof(riff_hdr_t) + sizeof(avih_hdr_t) +
230
return PJMEDIA_EINVALIMEDIATYPE;
234
status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[0]->fd);
235
if (status != PJ_SUCCESS)
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)
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))
251
status = PJMEDIA_EINVALIMEDIATYPE;
255
PJ_LOG(5, (THIS_FILE, "The AVI file has %d streams.",
256
avi_hdr.avih_hdr.num_streams));
258
/* Unsupported AVI format. */
259
if (avi_hdr.avih_hdr.num_streams > PJMEDIA_AVI_MAX_NUM_STREAMS) {
260
status = PJMEDIA_EAVIUNSUPP;
265
* TODO: Possibly unsupported AVI format.
266
* If you encounter this warning, verify whether the avi player
267
* is working properly.
269
if (avi_hdr.avih_hdr.flags & AVIF_MUSTUSEINDEX ||
270
avi_hdr.avih_hdr.pad > 1)
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));
277
/* Read the headers of each stream. */
278
for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
280
pj_ssize_t size_to_read;
282
/* Read strl header */
283
status = file_read(fport[0]->fd, &avi_hdr.strl_hdr[i],
285
if (status != PJ_SUCCESS)
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;
295
/* Read strf header */
296
status = file_read2(fport[0]->fd, &avi_hdr.strf_hdr[i],
298
if (status != PJ_SUCCESS)
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);
313
/* Skip the remainder of the header */
314
size_to_read = avi_hdr.strl_hdr[i].list_sz - (sizeof(strl_hdr_t) -
316
status = pj_file_setpos(fport[0]->fd, size_to_read, PJ_SEEK_CUR);
317
if (status != PJ_SUCCESS) {
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) {
329
/* Skip any JUNK or LIST INFO until we get MOVI tag */
331
pjmedia_avi_subchunk ch;
334
status = file_read(fport[0]->fd, &ch, sizeof(pjmedia_avi_subchunk));
335
if (status != PJ_SUCCESS) {
339
if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
342
status = file_read(fport[0]->fd, &ch, read);
343
if (COMPARE_TAG(ch.id, PJMEDIA_AVI_MOVI_TAG))
347
status = pj_file_setpos(fport[0]->fd, ch.len-read, PJ_SEEK_CUR);
348
if (status != PJ_SUCCESS) {
353
status = pj_file_getpos(fport[0]->fd, &pos);
354
if (status != PJ_SUCCESS)
357
for (i = 0, nstr = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
358
pjmedia_format_id fmt_id;
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)
370
if (COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
371
PJMEDIA_AVI_VIDS_TAG))
375
if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES) {
376
PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
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;
391
PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
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)
402
PJ_LOG(4, (THIS_FILE, "Unsupported audio stream"));
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;
412
/* Create fport instance. */
413
fport[nstr] = create_avi_port(pool);
420
status = pj_file_open(pool, filename, PJ_O_RDONLY,
422
if (status != PJ_SUCCESS)
425
/* Set the file position */
426
status = pj_file_setpos(fport[nstr]->fd, pos, PJ_SEEK_SET);
427
if (status != PJ_SUCCESS) {
432
fport[nstr]->stream_id = i;
433
fport[nstr]->fmt_id = fmt_id;
439
status = PJMEDIA_EAVIUNSUPP;
443
for (i = 0; i < nstr; i++) {
444
strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id];
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;
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;
457
vfi = pjmedia_get_video_format_info(
458
pjmedia_video_format_mgr_instance(),
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,
470
/* The calculation below is wrong. strf_hdr->biSizeImage shows
471
* uncompressed size. Looks like we need to go the ugly way to
473
* http://www.virtualdub.org/blog/pivot/entry.php?id=159
475
bps = strf_hdr->biSizeImage * 8 * strl_hdr->rate / strl_hdr->scale;
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;
482
fport[i]->base.info.fmt.det.vid.avg_bps = bps;
483
fport[i]->base.info.fmt.det.vid.max_bps = bps;
486
strf_audio_hdr_t *strf_hdr =
487
&avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr;
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,
493
strf_hdr->sample_rate,
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);
501
pj_strdup2(pool, &fport[i]->base.info.name, filename);
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;
513
"AVI file player '%.*s' created with "
515
(int)fport[0]->base.info.name.slen,
516
fport[0]->base.info.name.ptr,
517
(*p_streams)->num_streams));
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;
531
pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
534
return streams->num_streams;
537
PJ_DEF(pjmedia_avi_stream *)
538
pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
542
return (idx >=0 && idx < streams->num_streams ?
543
streams->streams[idx] : NULL);
546
PJ_DEF(pjmedia_avi_stream *)
547
pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
549
pjmedia_type media_type)
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];
562
* Get the data length, in bytes.
564
PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream)
566
struct avi_reader_port *fport;
569
PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
571
/* Check that this is really a player port */
572
PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
574
fport = (struct avi_reader_port*) stream;
576
return (pj_ssize_t)(fport->fsize - fport->start_data);
581
* Register a callback to be called when the file reading has reached the
585
pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
587
pj_status_t (*cb)(pjmedia_avi_stream *stream,
590
struct avi_reader_port *fport;
593
PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
595
/* Check that this is really a player port */
596
PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
598
fport = (struct avi_reader_port*) stream;
600
fport->base.port_data.pdata = user_data;
608
* Get frame from file.
610
static pj_status_t avi_get_frame(pjmedia_port *this_port,
611
pjmedia_frame *frame)
613
struct avi_reader_port *fport = (struct avi_reader_port*)this_port;
615
pj_ssize_t size_read = 0, size_to_read = 0;
617
pj_assert(fport->base.info.signature == SIGNATURE);
619
/* We encountered end of file */
621
pj_status_t status = PJ_SUCCESS;
623
PJ_LOG(5,(THIS_FILE, "File port %.*s EOF",
624
(int)fport->base.info.name.slen,
625
fport->base.info.name.ptr));
627
/* Call callback, if any */
629
status = (*fport->cb)(this_port, fport->base.port_data.pdata);
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).
635
if ((status != PJ_SUCCESS) ||
636
(fport->options & PJMEDIA_AVI_FILE_NO_LOOP))
638
frame->type = PJMEDIA_FRAME_TYPE_NONE;
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);
651
/* Fill frame buffer. */
652
size_to_read = frame->size;
654
pjmedia_avi_subchunk ch = {0, 0};
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)
664
size_to_read -= fport->size_left;
665
fport->size_left = 0;
668
/* Read new chunk data */
669
if (fport->size_left == 0) {
671
pj_file_getpos(fport->fd, &pos);
673
/* Data is padded to the nearest WORD boundary */
675
status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR);
679
status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk));
680
if (status != PJ_SUCCESS) {
685
cid = (char *)&ch.id;
686
if (cid[0] >= '0' && cid[0] <= '9' &&
687
cid[1] >= '0' && cid[1] <= '9')
689
stream_id = (cid[0] - '0') * 10 + (cid[1] - '0');
692
fport->pad = (pj_uint8_t)ch.len & 1;
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));
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 "
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."));
710
status = pj_file_setpos(fport->fd, ch.len,
714
fport->size_left = ch.len;
717
frame->type = (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO ?
718
PJMEDIA_FRAME_TYPE_VIDEO : PJMEDIA_FRAME_TYPE_AUDIO);
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)
728
fport->size_left -= size_to_read;
730
pj_assert(frame->size >= ch.len);
731
status = file_read3(fport->fd, frame->buf, ch.len,
733
if (status != PJ_SUCCESS)
735
frame->size = ch.len;
736
fport->size_left = 0;
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 /
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));
755
if (fport->usec_per_frame) {
756
fport->next_ts.u64 += (fport->usec_per_frame * VIDEO_CLOCK_RATE /
759
fport->next_ts.u64 += (frame->size * VIDEO_CLOCK_RATE /
760
(fport->base.info.fmt.det.vid.avg_bps / 8));
767
if (status == AVI_EOF) {
768
size_to_read -= size_read;
769
pj_bzero((char *)frame->buf + frame->size - size_to_read,
771
fport->eof = PJ_TRUE;
782
static pj_status_t avi_on_destroy(pjmedia_port *this_port)
784
struct avi_reader_port *fport = (struct avi_reader_port*) this_port;
786
pj_assert(this_port->info.signature == SIGNATURE);
788
if (fport->fd != (pj_oshandle_t) -1)
789
pj_file_close(fport->fd);
794
#endif /* PJMEDIA_HAS_VIDEO */