summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia/avi_player.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia/avi_player.c')
-rw-r--r--pjmedia/src/pjmedia/avi_player.c711
1 files changed, 711 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c
new file mode 100644
index 00000000..ce8d914a
--- /dev/null
+++ b/pjmedia/src/pjmedia/avi_player.c
@@ -0,0 +1,711 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * Default file player/writer buffer size.
+ */
+#include <pjmedia/avi_stream.h>
+#include <pjmedia/avi.h>
+#include <pjmedia/errno.h>
+#include <pjmedia/wave.h>
+#include <pj/assert.h>
+#include <pj/file_access.h>
+#include <pj/file_io.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "avi_player.c"
+
+#define AVIF_MUSTUSEINDEX 0x00000020
+#define AVIF_ISINTERLEAVED 0x00000100
+#define AVISF_DISABLED 0x00000001
+#define AVISF_VIDEO_PALCHANGES 0x00010000
+
+#define AVI_EOF 0xFFEEFFEE
+
+#define COMPARE_TAG(doc_tag, tag) (doc_tag == *((pj_uint32_t *)avi_tags[tag]))
+
+#define SIGNATURE PJMEDIA_SIG_PORT_VID_AVI_PLAYER
+
+#if 0
+# define TRACE_(x) PJ_LOG(4,x)
+#else
+# define TRACE_(x)
+#endif
+
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ static void data_to_host(void *data, pj_uint8_t bits, unsigned count)
+ {
+ unsigned i;
+ pj_int32_t *data32 = (pj_int32_t *)data;
+ pj_int16_t *data16 = (pj_int16_t *)data;
+ count /= (bits == 32? 4 : 2);
+ for (i=0; i<count; ++i) {
+ if (bits == 32)
+ data32[i] = pj_swap32(data32[i]);
+ else
+ data16[i] = pj_swap16(data16[i]);
+ }
+ }
+ static void data_to_host2(void *data, pj_uint8_t nsizes,
+ pj_uint8_t *sizes)
+ {
+ unsigned i;
+ pj_int8_t *datap = (pj_int8_t *)data;
+ for (i = 0; i < nsizes; i++) {
+ data_to_host(datap, 32, sizes[i]);
+ datap += sizes[i++];
+ if (i >= nsizes)
+ break;
+ data_to_host(datap, 16, sizes[i]);
+ datap += sizes[i];
+ }
+ }
+#else
+# define data_to_host(data, bits, count)
+# define data_to_host2(data, nsizes, sizes)
+#endif
+
+struct pjmedia_avi_streams
+{
+ unsigned num_streams;
+ pjmedia_port **streams;
+};
+
+struct avi_reader_port
+{
+ pjmedia_port base;
+ unsigned stream_id;
+ unsigned options;
+ pj_uint16_t bits_per_sample;
+ pj_bool_t eof;
+ pj_off_t fsize;
+ pj_off_t start_data;
+ pj_uint8_t pad;
+ pj_oshandle_t fd;
+ pj_ssize_t size_left;
+
+ pj_status_t (*cb)(pjmedia_port*, void*);
+};
+
+
+static pj_status_t avi_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame);
+static pj_status_t avi_on_destroy(pjmedia_port *this_port);
+
+static struct avi_reader_port *create_avi_port(pj_pool_t *pool)
+{
+ const pj_str_t name = pj_str("file");
+ struct avi_reader_port *port;
+
+ port = PJ_POOL_ZALLOC_T(pool, struct avi_reader_port);
+ if (!port)
+ return NULL;
+
+ /* Put in default values.
+ * These will be overriden once the file is read.
+ */
+ pjmedia_port_info_init(&port->base.info, &name, SIGNATURE,
+ 8000, 1, 16, 80);
+
+ port->fd = (pj_oshandle_t)-1;
+ port->base.get_frame = &avi_get_frame;
+ port->base.on_destroy = &avi_on_destroy;
+
+ return port;
+}
+
+#define file_read(fd, data, size) file_read2(fd, data, size, 32)
+#define file_read2(fd, data, size, bits) file_read3(fd, data, size, bits, NULL)
+
+static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size,
+ pj_uint16_t bits, pj_ssize_t *psz_read)
+{
+ pj_ssize_t size_read = size, size_to_read = size;
+ pj_status_t status = pj_file_read(fd, data, &size_read);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Normalize AVI header fields values from little-endian to host
+ * byte order.
+ */
+ if (bits > 0)
+ data_to_host(data, bits, size_read);
+
+ if (size_read != size_to_read) {
+ if (psz_read)
+ *psz_read = size_read;
+ return AVI_EOF;
+ }
+
+ return status;
+}
+
+/*
+ * Create AVI player port.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_avi_player_create_streams(pj_pool_t *pool,
+ const char *filename,
+ unsigned options,
+ pjmedia_avi_streams **p_streams)
+{
+ pjmedia_avi_hdr avi_hdr;
+ struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS];
+ pj_off_t pos;
+ unsigned i, nstr = 0;
+ pj_status_t status = PJ_SUCCESS;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL);
+
+ /* Check the file really exists. */
+ if (!pj_file_exists(filename)) {
+ return PJ_ENOTFOUND;
+ }
+
+ /* Create fport instance. */
+ fport[0] = create_avi_port(pool);
+ if (!fport[0]) {
+ return PJ_ENOMEM;
+ }
+
+ /* Get the file size. */
+ fport[0]->fsize = pj_file_size(filename);
+
+ /* Size must be more than AVI header size */
+ if (fport[0]->fsize <= sizeof(riff_hdr_t) + sizeof(avih_hdr_t) +
+ sizeof(strl_hdr_t))
+ {
+ return PJMEDIA_EINVALIMEDIATYPE;
+ }
+
+ /* Open file. */
+ status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[0]->fd);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Read the RIFF + AVIH header. */
+ status = file_read(fport[0]->fd, &avi_hdr,
+ sizeof(riff_hdr_t) + sizeof(avih_hdr_t));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Validate AVI file. */
+ if (!COMPARE_TAG(avi_hdr.riff_hdr.riff, PJMEDIA_AVI_RIFF_TAG) ||
+ !COMPARE_TAG(avi_hdr.riff_hdr.avi, PJMEDIA_AVI_AVI_TAG) ||
+ !COMPARE_TAG(avi_hdr.avih_hdr.list_tag, PJMEDIA_AVI_LIST_TAG) ||
+ !COMPARE_TAG(avi_hdr.avih_hdr.hdrl_tag, PJMEDIA_AVI_HDRL_TAG) ||
+ !COMPARE_TAG(avi_hdr.avih_hdr.avih, PJMEDIA_AVI_AVIH_TAG))
+ {
+ status = PJMEDIA_EINVALIMEDIATYPE;
+ goto on_error;
+ }
+
+ PJ_LOG(5, (THIS_FILE, "The AVI file has %d streams.",
+ avi_hdr.avih_hdr.num_streams));
+
+ /* Unsupported AVI format. */
+ if (avi_hdr.avih_hdr.num_streams > PJMEDIA_AVI_MAX_NUM_STREAMS) {
+ status = PJMEDIA_EAVIUNSUPP;
+ goto on_error;
+ }
+
+ /**
+ * TODO: Possibly unsupported AVI format.
+ * If you encounter this warning, verify whether the avi player
+ * is working properly.
+ */
+ if (avi_hdr.avih_hdr.flags & AVIF_MUSTUSEINDEX ||
+ avi_hdr.avih_hdr.pad > 1)
+ {
+ PJ_LOG(3, (THIS_FILE, "Warning!!! Possibly unsupported AVI format: "
+ "flags:%d, pad:%d", avi_hdr.avih_hdr.flags,
+ avi_hdr.avih_hdr.pad));
+ }
+
+ /* Read the headers of each stream. */
+ for (i = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
+ pj_size_t elem = 0;
+ pj_ssize_t size_to_read;
+
+ /* Read strl header */
+ status = file_read(fport[0]->fd, &avi_hdr.strl_hdr[i],
+ sizeof(strl_hdr_t));
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ elem = COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_VIDS_TAG) ?
+ sizeof(strf_video_hdr_t) :
+ COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_AUDS_TAG) ?
+ sizeof(strf_audio_hdr_t) : 0;
+
+ /* Read strf header */
+ status = file_read2(fport[0]->fd, &avi_hdr.strf_hdr[i],
+ elem, 0);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Normalize the endian */
+ if (elem == sizeof(strf_video_hdr_t))
+ data_to_host2(&avi_hdr.strf_hdr[i],
+ sizeof(strf_video_hdr_sizes)/
+ sizeof(strf_video_hdr_sizes[0]),
+ strf_video_hdr_sizes);
+ else if (elem == sizeof(strf_audio_hdr_t))
+ data_to_host2(&avi_hdr.strf_hdr[i],
+ sizeof(strf_audio_hdr_sizes)/
+ sizeof(strf_audio_hdr_sizes[0]),
+ strf_audio_hdr_sizes);
+
+ /* Skip the remainder of the header */
+ size_to_read = avi_hdr.strl_hdr[i].list_sz - (sizeof(strl_hdr_t) -
+ 8) - elem;
+ status = pj_file_setpos(fport[0]->fd, size_to_read, PJ_SEEK_CUR);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ }
+
+ /* Finish reading the AVIH header */
+ status = pj_file_setpos(fport[0]->fd, avi_hdr.avih_hdr.list_sz +
+ sizeof(riff_hdr_t) + 8, PJ_SEEK_SET);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Skip any JUNK or LIST INFO until we get MOVI tag */
+ do {
+ pjmedia_avi_subchunk ch;
+ int read = 0;
+
+ status = file_read(fport[0]->fd, &ch, sizeof(pjmedia_avi_subchunk));
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
+ {
+ read = 4;
+ status = file_read(fport[0]->fd, &ch, read);
+ if (COMPARE_TAG(ch.id, PJMEDIA_AVI_MOVI_TAG))
+ break;
+ }
+
+ status = pj_file_setpos(fport[0]->fd, ch.len-read, PJ_SEEK_CUR);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ } while(1);
+
+ status = pj_file_getpos(fport[0]->fd, &pos);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ for (i = 0, nstr = 0; i < avi_hdr.avih_hdr.num_streams; i++) {
+ /* Skip non-audio, non-video, or disabled streams) */
+ if ((!COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_VIDS_TAG) &&
+ !COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_AUDS_TAG)) ||
+ avi_hdr.strl_hdr[i].flags & AVISF_DISABLED)
+ {
+ continue;
+ }
+
+ if (COMPARE_TAG(avi_hdr.strl_hdr[i].data_type,
+ PJMEDIA_AVI_VIDS_TAG))
+ {
+ /* Check supported video formats here */
+ if (avi_hdr.strl_hdr[i].flags & AVISF_VIDEO_PALCHANGES ||
+ (avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_MJPEG &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_XVID &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_UYVY &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_YUY2 &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_IYUV &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_I420 &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_DIB &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_RGB24 &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_RGB32))
+ {
+ PJ_LOG(4, (THIS_FILE, "Unsupported video stream"));
+ continue;
+ }
+ } else {
+ /* Check supported audio formats here */
+ if ((avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_PCM &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ALAW &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_FORMAT_ULAW &&
+ avi_hdr.strl_hdr[i].codec != PJMEDIA_WAVE_FMT_TAG_PCM) ||
+ avi_hdr.strf_hdr[i].strf_audio_hdr.bits_per_sample != 16)
+ {
+ PJ_LOG(4, (THIS_FILE, "Unsupported audio stream"));
+ continue;
+ }
+ }
+
+ if (nstr == 0) {
+ fport[0]->stream_id = i;
+ nstr++;
+ continue;
+ }
+
+ /* Create fport instance. */
+ fport[nstr] = create_avi_port(pool);
+ if (!fport[nstr]) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ fport[nstr]->stream_id = i;
+
+ /* Open file. */
+ status = pj_file_open(pool, filename, PJ_O_RDONLY, &fport[nstr]->fd);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Set the file position */
+ status = pj_file_setpos(fport[nstr]->fd, pos, PJ_SEEK_SET);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ nstr++;
+ }
+
+ if (nstr == 0) {
+ status = PJMEDIA_EAVIUNSUPP;
+ goto on_error;
+ }
+
+ for (i = 0; i < nstr; i++) {
+ strl_hdr_t *strl_hdr = &avi_hdr.strl_hdr[fport[i]->stream_id];
+
+ /* Initialize */
+ fport[i]->options = options;
+ fport[i]->fsize = fport[0]->fsize;
+ /* Current file position now points to start of data */
+ fport[i]->start_data = pos;
+
+ if (COMPARE_TAG(strl_hdr->data_type, PJMEDIA_AVI_VIDS_TAG)) {
+ strf_video_hdr_t *strf_hdr =
+ &avi_hdr.strf_hdr[fport[i]->stream_id].strf_video_hdr;
+ const pjmedia_video_format_info *vfi;
+
+ vfi = pjmedia_get_video_format_info(
+ pjmedia_video_format_mgr_instance(),
+ strl_hdr->codec);
+
+ fport[i]->bits_per_sample = (vfi ? vfi->bpp : 0);
+ pjmedia_format_init_video(&fport[i]->base.info.fmt,
+ strl_hdr->codec,
+ strf_hdr->biWidth,
+ strf_hdr->biHeight,
+ strl_hdr->rate,
+ strl_hdr->scale);
+
+ } else {
+ strf_audio_hdr_t *strf_hdr =
+ &avi_hdr.strf_hdr[fport[i]->stream_id].strf_audio_hdr;
+
+ fport[i]->bits_per_sample = strf_hdr->bits_per_sample;
+ pjmedia_format_init_audio(&fport[i]->base.info.fmt,
+ strl_hdr->codec,
+ strf_hdr->sample_rate,
+ strf_hdr->nchannels,
+ strf_hdr->bits_per_sample,
+ 20000,
+ strf_hdr->bytes_per_sec,
+ strf_hdr->bytes_per_sec);
+ }
+
+ pj_strdup2(pool, &fport[i]->base.info.name, filename);
+ }
+
+ /* Done. */
+ *p_streams = pj_pool_alloc(pool, sizeof(pjmedia_avi_streams));
+ (*p_streams)->num_streams = nstr;
+ (*p_streams)->streams = pj_pool_calloc(pool, (*p_streams)->num_streams,
+ sizeof(pjmedia_port *));
+ for (i = 0; i < nstr; i++)
+ (*p_streams)->streams[i] = &fport[i]->base;
+
+ PJ_LOG(4,(THIS_FILE,
+ "AVI file player '%.*s' created with "
+ "%d media ports",
+ (int)fport[0]->base.info.name.slen,
+ fport[0]->base.info.name.ptr,
+ (*p_streams)->num_streams));
+
+ return PJ_SUCCESS;
+
+on_error:
+ fport[0]->base.on_destroy(&fport[0]->base);
+ for (i = 1; i < nstr; i++)
+ fport[i]->base.on_destroy(&fport[i]->base);
+ if (status == AVI_EOF)
+ return PJMEDIA_EINVALIMEDIATYPE;
+ return status;
+}
+
+PJ_DEF(unsigned)
+pjmedia_avi_streams_get_num_streams(pjmedia_avi_streams *streams)
+{
+ pj_assert(streams);
+ return streams->num_streams;
+}
+
+PJ_DEF(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream(pjmedia_avi_streams *streams,
+ unsigned idx)
+{
+ pj_assert(streams);
+ return (idx >=0 && idx < streams->num_streams ?
+ streams->streams[idx] : NULL);
+}
+
+PJ_DEF(pjmedia_avi_stream *)
+pjmedia_avi_streams_get_stream_by_media(pjmedia_avi_streams *streams,
+ unsigned start_idx,
+ pjmedia_type media_type)
+{
+ unsigned i;
+
+ pj_assert(streams);
+ for (i = start_idx; i < streams->num_streams; i++)
+ if (streams->streams[i]->info.fmt.type == media_type)
+ return streams->streams[i];
+ return NULL;
+}
+
+
+/*
+ * Get the data length, in bytes.
+ */
+PJ_DEF(pj_ssize_t) pjmedia_avi_stream_get_len(pjmedia_avi_stream *stream)
+{
+ struct avi_reader_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct avi_reader_port*) stream;
+
+ return (pj_ssize_t)(fport->fsize - fport->start_data);
+}
+
+
+/*
+ * Register a callback to be called when the file reading has reached the
+ * end of file.
+ */
+PJ_DEF(pj_status_t)
+pjmedia_avi_stream_set_eof_cb( pjmedia_avi_stream *stream,
+ void *user_data,
+ pj_status_t (*cb)(pjmedia_avi_stream *stream,
+ void *usr_data))
+{
+ struct avi_reader_port *fport;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(stream, -PJ_EINVAL);
+
+ /* Check that this is really a player port */
+ PJ_ASSERT_RETURN(stream->info.signature == SIGNATURE, -PJ_EINVALIDOP);
+
+ fport = (struct avi_reader_port*) stream;
+
+ fport->base.port_data.pdata = user_data;
+ fport->cb = cb;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Get frame from file.
+ */
+static pj_status_t avi_get_frame(pjmedia_port *this_port,
+ pjmedia_frame *frame)
+{
+ struct avi_reader_port *fport = (struct avi_reader_port*)this_port;
+ pj_status_t status;
+ pj_ssize_t size_read = 0, size_to_read = 0;
+
+ pj_assert(fport->base.info.signature == SIGNATURE);
+
+ /* We encountered end of file */
+ if (fport->eof) {
+ pj_status_t status = PJ_SUCCESS;
+
+ PJ_LOG(5,(THIS_FILE, "File port %.*s EOF",
+ (int)fport->base.info.name.slen,
+ fport->base.info.name.ptr));
+
+ /* Call callback, if any */
+ if (fport->cb)
+ status = (*fport->cb)(this_port, fport->base.port_data.pdata);
+
+ /* If callback returns non PJ_SUCCESS or 'no loop' is specified,
+ * return immediately (and don't try to access player port since
+ * it might have been destroyed by the callback).
+ */
+ if ((status != PJ_SUCCESS) ||
+ (fport->options & PJMEDIA_AVI_FILE_NO_LOOP))
+ {
+ frame->type = PJMEDIA_FRAME_TYPE_NONE;
+ frame->size = 0;
+ return PJ_EEOF;
+ }
+
+ /* Rewind file */
+ PJ_LOG(5,(THIS_FILE, "File port %.*s rewinding..",
+ (int)fport->base.info.name.slen,
+ fport->base.info.name.ptr));
+ fport->eof = PJ_FALSE;
+ pj_file_setpos(fport->fd, fport->start_data, PJ_SEEK_SET);
+ }
+
+ /* Fill frame buffer. */
+ size_to_read = frame->size;
+ do {
+ pjmedia_avi_subchunk ch = {0, 0};
+ char *cid;
+ unsigned stream_id;
+
+ /* We need to read data from the file past the chunk boundary */
+ if (fport->size_left > 0 && fport->size_left < size_to_read) {
+ status = file_read3(fport->fd, frame->buf, fport->size_left,
+ fport->bits_per_sample, &size_read);
+ if (status != PJ_SUCCESS)
+ goto on_error2;
+ size_to_read -= fport->size_left;
+ fport->size_left = 0;
+ }
+
+ /* Read new chunk data */
+ if (fport->size_left == 0) {
+ pj_off_t pos;
+ pj_file_getpos(fport->fd, &pos);
+
+ /* Data is padded to the nearest WORD boundary */
+ if (fport->pad) {
+ status = pj_file_setpos(fport->fd, fport->pad, PJ_SEEK_CUR);
+ fport->pad = 0;
+ }
+
+ status = file_read(fport->fd, &ch, sizeof(pjmedia_avi_subchunk));
+ if (status != PJ_SUCCESS) {
+ size_read = 0;
+ goto on_error2;
+ }
+
+ cid = (char *)&ch.id;
+ if (cid[0] >= '0' && cid[0] <= '9' &&
+ cid[1] >= '0' && cid[1] <= '9')
+ {
+ stream_id = (cid[0] - '0') * 10 + (cid[1] - '0');
+ } else
+ stream_id = 100;
+ fport->pad = (pj_uint8_t)ch.len & 1;
+
+ TRACE_((THIS_FILE, "Reading movi data at pos %u (%x), id: %.*s, "
+ "length: %u", (unsigned long)pos,
+ (unsigned long)pos, 4, cid, ch.len));
+
+ /* We are only interested in data with our stream id */
+ if (stream_id != fport->stream_id) {
+ if (COMPARE_TAG(ch.id, PJMEDIA_AVI_LIST_TAG))
+ PJ_LOG(5, (THIS_FILE, "Unsupported LIST tag found in "
+ "the movi data."));
+ else if (COMPARE_TAG(ch.id, PJMEDIA_AVI_RIFF_TAG)) {
+ PJ_LOG(3, (THIS_FILE, "Unsupported format: multiple "
+ "AVIs in a single file."));
+ status = AVI_EOF;
+ goto on_error2;
+ }
+
+ status = pj_file_setpos(fport->fd, ch.len,
+ PJ_SEEK_CUR);
+ continue;
+ }
+ fport->size_left = ch.len;
+ }
+
+ frame->type = (fport->base.info.fmt.type == PJMEDIA_TYPE_VIDEO ?
+ PJMEDIA_FRAME_TYPE_VIDEO : PJMEDIA_FRAME_TYPE_AUDIO);
+ frame->timestamp.u64 = 0;
+ if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {
+ if (size_to_read > fport->size_left)
+ size_to_read = fport->size_left;
+ status = file_read3(fport->fd, (char *)frame->buf + frame->size -
+ size_to_read, size_to_read,
+ fport->bits_per_sample, &size_read);
+ if (status != PJ_SUCCESS)
+ goto on_error2;
+ fport->size_left -= size_to_read;
+ } else {
+ pj_assert(frame->size >= ch.len);
+ status = file_read3(fport->fd, frame->buf, ch.len,
+ 0, &size_read);
+ if (status != PJ_SUCCESS)
+ goto on_error2;
+ frame->size = ch.len;
+ fport->size_left = 0;
+ }
+
+ break;
+
+ } while(1);
+
+ return PJ_SUCCESS;
+
+on_error2:
+ if (status == AVI_EOF) {
+ size_to_read -= size_read;
+ pj_bzero((char *)frame->buf + frame->size - size_to_read,
+ size_to_read);
+ fport->eof = PJ_TRUE;
+
+ return PJ_SUCCESS;
+ }
+
+ return status;
+}
+
+/*
+ * Destroy port.
+ */
+static pj_status_t avi_on_destroy(pjmedia_port *this_port)
+{
+ struct avi_reader_port *fport = (struct avi_reader_port*) this_port;
+
+ pj_assert(this_port->info.signature == SIGNATURE);
+
+ if (fport->fd != (pj_oshandle_t) -1)
+ pj_file_close(fport->fd);
+ return PJ_SUCCESS;
+}