~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjmedia/src/pjmedia/avi_player.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: avi_player.c 4057 2012-04-17 06:54:50Z bennylp $ */
 
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 */