~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* 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 */