Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/*
2
 * GXF demuxer.
3
 * Copyright (c) 2006 Reimar Doeffinger
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
 
22
#include "libavutil/channel_layout.h"
23
#include "libavutil/common.h"
24
#include "avformat.h"
25
#include "internal.h"
26
#include "gxf.h"
27
#include "libavcodec/mpeg12data.h"
28
 
29
struct gxf_stream_info {
30
    int64_t first_field;
31
    int64_t last_field;
32
    AVRational frames_per_second;
33
    int32_t fields_per_frame;
34
    int64_t track_aux_data;
35
};
36
 
37
/**
38
 * @brief parse gxf timecode and add it to metadata
39
 */
40
static int add_timecode_metadata(AVDictionary **pm, const char *key, uint32_t timecode, int fields_per_frame)
41
{
42
   char tmp[128];
43
   int field  = timecode & 0xff;
44
   int frame  = fields_per_frame ? field / fields_per_frame : field;
45
   int second = (timecode >>  8) & 0xff;
46
   int minute = (timecode >> 16) & 0xff;
47
   int hour   = (timecode >> 24) & 0x1f;
48
   int drop   = (timecode >> 29) & 1;
49
   // bit 30: color_frame, unused
50
   // ignore invalid time code
51
   if (timecode >> 31)
52
       return 0;
53
   snprintf(tmp, sizeof(tmp), "%02d:%02d:%02d%c%02d",
54
       hour, minute, second, drop ? ';' : ':', frame);
55
   return av_dict_set(pm, key, tmp, 0);
56
}
57
 
58
/**
59
 * @brief parses a packet header, extracting type and length
60
 * @param pb AVIOContext to read header from
61
 * @param type detected packet type is stored here
62
 * @param length detected packet length, excluding header is stored here
63
 * @return 0 if header not found or contains invalid data, 1 otherwise
64
 */
65
static int parse_packet_header(AVIOContext *pb, GXFPktType *type, int *length) {
66
    if (avio_rb32(pb))
67
        return 0;
68
    if (avio_r8(pb) != 1)
69
        return 0;
70
    *type = avio_r8(pb);
71
    *length = avio_rb32(pb);
72
    if ((*length >> 24) || *length < 16)
73
        return 0;
74
    *length -= 16;
75
    if (avio_rb32(pb))
76
        return 0;
77
    if (avio_r8(pb) != 0xe1)
78
        return 0;
79
    if (avio_r8(pb) != 0xe2)
80
        return 0;
81
    return 1;
82
}
83
 
84
/**
85
 * @brief check if file starts with a PKT_MAP header
86
 */
87
static int gxf_probe(AVProbeData *p) {
88
    static const uint8_t startcode[] = {0, 0, 0, 0, 1, 0xbc}; // start with map packet
89
    static const uint8_t endcode[] = {0, 0, 0, 0, 0xe1, 0xe2};
90
    if (!memcmp(p->buf, startcode, sizeof(startcode)) &&
91
        !memcmp(&p->buf[16 - sizeof(endcode)], endcode, sizeof(endcode)))
92
        return AVPROBE_SCORE_MAX;
93
    return 0;
94
}
95
 
96
/**
97
 * @brief gets the stream index for the track with the specified id, creates new
98
 *        stream if not found
99
 * @param id     id of stream to find / add
100
 * @param format stream format identifier
101
 */
102
static int get_sindex(AVFormatContext *s, int id, int format) {
103
    int i;
104
    AVStream *st = NULL;
105
    i = ff_find_stream_index(s, id);
106
    if (i >= 0)
107
        return i;
108
    st = avformat_new_stream(s, NULL);
109
    if (!st)
110
        return AVERROR(ENOMEM);
111
    st->id = id;
112
    switch (format) {
113
        case 3:
114
        case 4:
115
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
116
            st->codec->codec_id = AV_CODEC_ID_MJPEG;
117
            break;
118
        case 13:
119
        case 14:
120
        case 15:
121
        case 16:
122
        case 25:
123
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
124
            st->codec->codec_id = AV_CODEC_ID_DVVIDEO;
125
            break;
126
        case 11:
127
        case 12:
128
        case 20:
129
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
130
            st->codec->codec_id = AV_CODEC_ID_MPEG2VIDEO;
131
            st->need_parsing = AVSTREAM_PARSE_HEADERS; //get keyframe flag etc.
132
            break;
133
        case 22:
134
        case 23:
135
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
136
            st->codec->codec_id = AV_CODEC_ID_MPEG1VIDEO;
137
            st->need_parsing = AVSTREAM_PARSE_HEADERS; //get keyframe flag etc.
138
            break;
139
        case 9:
140
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
141
            st->codec->codec_id = AV_CODEC_ID_PCM_S24LE;
142
            st->codec->channels = 1;
143
            st->codec->channel_layout = AV_CH_LAYOUT_MONO;
144
            st->codec->sample_rate = 48000;
145
            st->codec->bit_rate = 3 * 1 * 48000 * 8;
146
            st->codec->block_align = 3 * 1;
147
            st->codec->bits_per_coded_sample = 24;
148
            break;
149
        case 10:
150
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
151
            st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
152
            st->codec->channels = 1;
153
            st->codec->channel_layout = AV_CH_LAYOUT_MONO;
154
            st->codec->sample_rate = 48000;
155
            st->codec->bit_rate = 2 * 1 * 48000 * 8;
156
            st->codec->block_align = 2 * 1;
157
            st->codec->bits_per_coded_sample = 16;
158
            break;
159
        case 17:
160
            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
161
            st->codec->codec_id = AV_CODEC_ID_AC3;
162
            st->codec->channels = 2;
163
            st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
164
            st->codec->sample_rate = 48000;
165
            break;
166
        case 26: /* AVCi50 / AVCi100 (AVC Intra) */
167
        case 29: /* AVCHD */
168
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
169
            st->codec->codec_id = CODEC_ID_H264;
170
            st->need_parsing = AVSTREAM_PARSE_HEADERS;
171
            break;
172
        // timecode tracks:
173
        case 7:
174
        case 8:
175
        case 24:
176
            st->codec->codec_type = AVMEDIA_TYPE_DATA;
177
            st->codec->codec_id = AV_CODEC_ID_NONE;
178
            break;
179
        case 30:
180
            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
181
            st->codec->codec_id = AV_CODEC_ID_DNXHD;
182
            break;
183
        default:
184
            st->codec->codec_type = AVMEDIA_TYPE_UNKNOWN;
185
            st->codec->codec_id = AV_CODEC_ID_NONE;
186
            break;
187
    }
188
    return s->nb_streams - 1;
189
}
190
 
191
/**
192
 * @brief filters out interesting tags from material information.
193
 * @param len length of tag section, will be adjusted to contain remaining bytes
194
 * @param si struct to store collected information into
195
 */
196
static void gxf_material_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) {
197
    si->first_field = AV_NOPTS_VALUE;
198
    si->last_field = AV_NOPTS_VALUE;
199
    while (*len >= 2) {
200
        GXFMatTag tag = avio_r8(pb);
201
        int tlen = avio_r8(pb);
202
        *len -= 2;
203
        if (tlen > *len)
204
            return;
205
        *len -= tlen;
206
        if (tlen == 4) {
207
            uint32_t value = avio_rb32(pb);
208
            if (tag == MAT_FIRST_FIELD)
209
                si->first_field = value;
210
            else if (tag == MAT_LAST_FIELD)
211
                si->last_field = value;
212
        } else
213
            avio_skip(pb, tlen);
214
    }
215
}
216
 
217
static const AVRational frame_rate_tab[] = {
218
    {   60,    1},
219
    {60000, 1001},
220
    {   50,    1},
221
    {   30,    1},
222
    {30000, 1001},
223
    {   25,    1},
224
    {   24,    1},
225
    {24000, 1001},
226
    {    0,    0},
227
};
228
 
229
/**
230
 * @brief convert fps tag value to AVRational fps
231
 * @param fps fps value from tag
232
 * @return fps as AVRational, or 0 / 0 if unknown
233
 */
234
static AVRational fps_tag2avr(int32_t fps) {
235
    if (fps < 1 || fps > 9) fps = 9;
236
    return frame_rate_tab[fps - 1];
237
}
238
 
239
/**
240
 * @brief convert UMF attributes flags to AVRational fps
241
 * @param flags UMF flags to convert
242
 * @return fps as AVRational, or 0 / 0 if unknown
243
 */
244
static AVRational fps_umf2avr(uint32_t flags) {
245
    static const AVRational map[] = {{50, 1}, {60000, 1001}, {24, 1},
246
        {25, 1}, {30000, 1001}};
247
    int idx =  av_log2((flags & 0x7c0) >> 6);
248
    return map[idx];
249
}
250
 
251
/**
252
 * @brief filters out interesting tags from track information.
253
 * @param len length of tag section, will be adjusted to contain remaining bytes
254
 * @param si struct to store collected information into
255
 */
256
static void gxf_track_tags(AVIOContext *pb, int *len, struct gxf_stream_info *si) {
257
    si->frames_per_second = (AVRational){0, 0};
258
    si->fields_per_frame = 0;
259
    si->track_aux_data = 0x80000000;
260
    while (*len >= 2) {
261
        GXFTrackTag tag = avio_r8(pb);
262
        int tlen = avio_r8(pb);
263
        *len -= 2;
264
        if (tlen > *len)
265
            return;
266
        *len -= tlen;
267
        if (tlen == 4) {
268
            uint32_t value = avio_rb32(pb);
269
            if (tag == TRACK_FPS)
270
                si->frames_per_second = fps_tag2avr(value);
271
            else if (tag == TRACK_FPF && (value == 1 || value == 2))
272
                si->fields_per_frame = value;
273
        } else if (tlen == 8 && tag == TRACK_AUX)
274
            si->track_aux_data = avio_rl64(pb);
275
        else
276
            avio_skip(pb, tlen);
277
    }
278
}
279
 
280
/**
281
 * @brief read index from FLT packet into stream 0 av_index
282
 */
283
static void gxf_read_index(AVFormatContext *s, int pkt_len) {
284
    AVIOContext *pb = s->pb;
285
    AVStream *st;
286
    uint32_t fields_per_map = avio_rl32(pb);
287
    uint32_t map_cnt = avio_rl32(pb);
288
    int i;
289
    pkt_len -= 8;
290
    if ((s->flags & AVFMT_FLAG_IGNIDX) || !s->streams) {
291
        avio_skip(pb, pkt_len);
292
        return;
293
    }
294
    st = s->streams[0];
295
    if (map_cnt > 1000) {
296
        av_log(s, AV_LOG_ERROR, "too many index entries %u (%x)\n", map_cnt, map_cnt);
297
        map_cnt = 1000;
298
    }
299
    if (pkt_len < 4 * map_cnt) {
300
        av_log(s, AV_LOG_ERROR, "invalid index length\n");
301
        avio_skip(pb, pkt_len);
302
        return;
303
    }
304
    pkt_len -= 4 * map_cnt;
305
    av_add_index_entry(st, 0, 0, 0, 0, 0);
306
    for (i = 0; i < map_cnt; i++)
307
        av_add_index_entry(st, (uint64_t)avio_rl32(pb) * 1024,
308
                           i * (uint64_t)fields_per_map + 1, 0, 0, 0);
309
    avio_skip(pb, pkt_len);
310
}
311
 
312
static int gxf_header(AVFormatContext *s) {
313
    AVIOContext *pb = s->pb;
314
    GXFPktType pkt_type;
315
    int map_len;
316
    int len;
317
    AVRational main_timebase = {0, 0};
318
    struct gxf_stream_info *si = s->priv_data;
319
    int i;
320
    if (!parse_packet_header(pb, &pkt_type, &map_len) || pkt_type != PKT_MAP) {
321
        av_log(s, AV_LOG_ERROR, "map packet not found\n");
322
        return 0;
323
    }
324
    map_len -= 2;
325
    if (avio_r8(pb) != 0x0e0 || avio_r8(pb) != 0xff) {
326
        av_log(s, AV_LOG_ERROR, "unknown version or invalid map preamble\n");
327
        return 0;
328
    }
329
    map_len -= 2;
330
    len = avio_rb16(pb); // length of material data section
331
    if (len > map_len) {
332
        av_log(s, AV_LOG_ERROR, "material data longer than map data\n");
333
        return 0;
334
    }
335
    map_len -= len;
336
    gxf_material_tags(pb, &len, si);
337
    avio_skip(pb, len);
338
    map_len -= 2;
339
    len = avio_rb16(pb); // length of track description
340
    if (len > map_len) {
341
        av_log(s, AV_LOG_ERROR, "track description longer than map data\n");
342
        return 0;
343
    }
344
    map_len -= len;
345
    while (len > 0) {
346
        int track_type, track_id, track_len;
347
        AVStream *st;
348
        int idx;
349
        len -= 4;
350
        track_type = avio_r8(pb);
351
        track_id = avio_r8(pb);
352
        track_len = avio_rb16(pb);
353
        len -= track_len;
354
        if (!(track_type & 0x80)) {
355
           av_log(s, AV_LOG_ERROR, "invalid track type %x\n", track_type);
356
           continue;
357
        }
358
        track_type &= 0x7f;
359
        if ((track_id & 0xc0) != 0xc0) {
360
           av_log(s, AV_LOG_ERROR, "invalid track id %x\n", track_id);
361
           continue;
362
        }
363
        track_id &= 0x3f;
364
        gxf_track_tags(pb, &track_len, si);
365
        // check for timecode tracks
366
        if (track_type == 7 || track_type == 8 || track_type == 24) {
367
            add_timecode_metadata(&s->metadata, "timecode",
368
                                  si->track_aux_data & 0xffffffff,
369
                                  si->fields_per_frame);
370
 
371
        }
372
        avio_skip(pb, track_len);
373
 
374
        idx = get_sindex(s, track_id, track_type);
375
        if (idx < 0) continue;
376
        st = s->streams[idx];
377
        if (!main_timebase.num || !main_timebase.den) {
378
            main_timebase.num = si->frames_per_second.den;
379
            main_timebase.den = si->frames_per_second.num * 2;
380
        }
381
        st->start_time = si->first_field;
382
        if (si->first_field != AV_NOPTS_VALUE && si->last_field != AV_NOPTS_VALUE)
383
            st->duration = si->last_field - si->first_field;
384
    }
385
    if (len < 0)
386
        av_log(s, AV_LOG_ERROR, "invalid track description length specified\n");
387
    if (map_len)
388
        avio_skip(pb, map_len);
389
    if (!parse_packet_header(pb, &pkt_type, &len)) {
390
        av_log(s, AV_LOG_ERROR, "sync lost in header\n");
391
        return -1;
392
    }
393
    if (pkt_type == PKT_FLT) {
394
        gxf_read_index(s, len);
395
        if (!parse_packet_header(pb, &pkt_type, &len)) {
396
            av_log(s, AV_LOG_ERROR, "sync lost in header\n");
397
            return -1;
398
        }
399
    }
400
    if (pkt_type == PKT_UMF) {
401
        if (len >= 0x39) {
402
            AVRational fps;
403
            len -= 0x39;
404
            avio_skip(pb, 5); // preamble
405
            avio_skip(pb, 0x30); // payload description
406
            fps = fps_umf2avr(avio_rl32(pb));
407
            if (!main_timebase.num || !main_timebase.den) {
408
                av_log(s, AV_LOG_WARNING, "No FPS track tag, using UMF fps tag."
409
                                          " This might give wrong results.\n");
410
                // this may not always be correct, but simply the best we can get
411
                main_timebase.num = fps.den;
412
                main_timebase.den = fps.num * 2;
413
            }
414
 
415
            if (len >= 0x18) {
416
                len -= 0x18;
417
                avio_skip(pb, 0x10);
418
                add_timecode_metadata(&s->metadata, "timecode_at_mark_in",
419
                                      avio_rl32(pb), si->fields_per_frame);
420
                add_timecode_metadata(&s->metadata, "timecode_at_mark_out",
421
                                      avio_rl32(pb), si->fields_per_frame);
422
            }
423
        } else
424
            av_log(s, AV_LOG_INFO, "UMF packet too short\n");
425
    } else
426
        av_log(s, AV_LOG_INFO, "UMF packet missing\n");
427
    avio_skip(pb, len);
428
    // set a fallback value, 60000/1001 is specified for audio-only files
429
    // so use that regardless of why we do not know the video frame rate.
430
    if (!main_timebase.num || !main_timebase.den)
431
        main_timebase = (AVRational){1001, 60000};
432
    for (i = 0; i < s->nb_streams; i++) {
433
        AVStream *st = s->streams[i];
434
        avpriv_set_pts_info(st, 32, main_timebase.num, main_timebase.den);
435
    }
436
    return 0;
437
}
438
 
439
#define READ_ONE() \
440
    { \
441
        if (!max_interval-- || url_feof(pb)) \
442
            goto out; \
443
        tmp = tmp << 8 | avio_r8(pb); \
444
    }
445
 
446
/**
447
 * @brief resync the stream on the next media packet with specified properties
448
 * @param max_interval how many bytes to search for matching packet at most
449
 * @param track track id the media packet must belong to, -1 for any
450
 * @param timestamp minimum timestamp (== field number) the packet must have, -1 for any
451
 * @return timestamp of packet found
452
 */
453
static int64_t gxf_resync_media(AVFormatContext *s, uint64_t max_interval, int track, int timestamp) {
454
    uint32_t tmp;
455
    uint64_t last_pos;
456
    uint64_t last_found_pos = 0;
457
    int cur_track;
458
    int64_t cur_timestamp = AV_NOPTS_VALUE;
459
    int len;
460
    AVIOContext *pb = s->pb;
461
    GXFPktType type;
462
    tmp = avio_rb32(pb);
463
start:
464
    while (tmp)
465
        READ_ONE();
466
    READ_ONE();
467
    if (tmp != 1)
468
        goto start;
469
    last_pos = avio_tell(pb);
470
    if (avio_seek(pb, -5, SEEK_CUR) < 0)
471
        goto out;
472
    if (!parse_packet_header(pb, &type, &len) || type != PKT_MEDIA) {
473
        if (avio_seek(pb, last_pos, SEEK_SET) < 0)
474
            goto out;
475
        goto start;
476
    }
477
    avio_r8(pb);
478
    cur_track = avio_r8(pb);
479
    cur_timestamp = avio_rb32(pb);
480
    last_found_pos = avio_tell(pb) - 16 - 6;
481
    if ((track >= 0 && track != cur_track) || (timestamp >= 0 && timestamp > cur_timestamp)) {
482
        if (avio_seek(pb, last_pos, SEEK_SET) >= 0)
483
            goto start;
484
    }
485
out:
486
    if (last_found_pos)
487
        avio_seek(pb, last_found_pos, SEEK_SET);
488
    return cur_timestamp;
489
}
490
 
491
static int gxf_packet(AVFormatContext *s, AVPacket *pkt) {
492
    AVIOContext *pb = s->pb;
493
    GXFPktType pkt_type;
494
    int pkt_len;
495
    struct gxf_stream_info *si = s->priv_data;
496
 
497
    while (!pb->eof_reached) {
498
        AVStream *st;
499
        int track_type, track_id, ret;
500
        int field_nr, field_info, skip = 0;
501
        int stream_index;
502
        if (!parse_packet_header(pb, &pkt_type, &pkt_len)) {
503
            if (!url_feof(pb))
504
                av_log(s, AV_LOG_ERROR, "sync lost\n");
505
            return -1;
506
        }
507
        if (pkt_type == PKT_FLT) {
508
            gxf_read_index(s, pkt_len);
509
            continue;
510
        }
511
        if (pkt_type != PKT_MEDIA) {
512
            avio_skip(pb, pkt_len);
513
            continue;
514
        }
515
        if (pkt_len < 16) {
516
            av_log(s, AV_LOG_ERROR, "invalid media packet length\n");
517
            continue;
518
        }
519
        pkt_len -= 16;
520
        track_type = avio_r8(pb);
521
        track_id = avio_r8(pb);
522
        stream_index = get_sindex(s, track_id, track_type);
523
        if (stream_index < 0)
524
            return stream_index;
525
        st = s->streams[stream_index];
526
        field_nr = avio_rb32(pb);
527
        field_info = avio_rb32(pb);
528
        avio_rb32(pb); // "timeline" field number
529
        avio_r8(pb); // flags
530
        avio_r8(pb); // reserved
531
        if (st->codec->codec_id == AV_CODEC_ID_PCM_S24LE ||
532
            st->codec->codec_id == AV_CODEC_ID_PCM_S16LE) {
533
            int first = field_info >> 16;
534
            int last  = field_info & 0xffff; // last is exclusive
535
            int bps = av_get_bits_per_sample(st->codec->codec_id)>>3;
536
            if (first <= last && last*bps <= pkt_len) {
537
                avio_skip(pb, first*bps);
538
                skip = pkt_len - last*bps;
539
                pkt_len = (last-first)*bps;
540
            } else
541
                av_log(s, AV_LOG_ERROR, "invalid first and last sample values\n");
542
        }
543
        ret = av_get_packet(pb, pkt, pkt_len);
544
        if (skip)
545
            avio_skip(pb, skip);
546
        pkt->stream_index = stream_index;
547
        pkt->dts = field_nr;
548
 
549
        //set duration manually for DV or else lavf misdetects the frame rate
550
        if (st->codec->codec_id == AV_CODEC_ID_DVVIDEO)
551
            pkt->duration = si->fields_per_frame;
552
 
553
        return ret;
554
    }
555
    return AVERROR_EOF;
556
}
557
 
558
static int gxf_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) {
559
    int res = 0;
560
    uint64_t pos;
561
    uint64_t maxlen = 100 * 1024 * 1024;
562
    AVStream *st = s->streams[0];
563
    int64_t start_time = s->streams[stream_index]->start_time;
564
    int64_t found;
565
    int idx;
566
    if (timestamp < start_time) timestamp = start_time;
567
    idx = av_index_search_timestamp(st, timestamp - start_time,
568
                                    AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD);
569
    if (idx < 0)
570
        return -1;
571
    pos = st->index_entries[idx].pos;
572
    if (idx < st->nb_index_entries - 2)
573
        maxlen = st->index_entries[idx + 2].pos - pos;
574
    maxlen = FFMAX(maxlen, 200 * 1024);
575
    res = avio_seek(s->pb, pos, SEEK_SET);
576
    if (res < 0)
577
        return res;
578
    found = gxf_resync_media(s, maxlen, -1, timestamp);
579
    if (FFABS(found - timestamp) > 4)
580
        return -1;
581
    return 0;
582
}
583
 
584
static int64_t gxf_read_timestamp(AVFormatContext *s, int stream_index,
585
                                  int64_t *pos, int64_t pos_limit) {
586
    AVIOContext *pb = s->pb;
587
    int64_t res;
588
    if (avio_seek(pb, *pos, SEEK_SET) < 0)
589
        return AV_NOPTS_VALUE;
590
    res = gxf_resync_media(s, pos_limit - *pos, -1, -1);
591
    *pos = avio_tell(pb);
592
    return res;
593
}
594
 
595
AVInputFormat ff_gxf_demuxer = {
596
    .name           = "gxf",
597
    .long_name      = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"),
598
    .priv_data_size = sizeof(struct gxf_stream_info),
599
    .read_probe     = gxf_probe,
600
    .read_header    = gxf_header,
601
    .read_packet    = gxf_packet,
602
    .read_seek      = gxf_seek,
603
    .read_timestamp = gxf_read_timestamp,
604
};