xref: /third_party/ffmpeg/libavformat/bfi.c (revision cabdff1a)
1/*
2 * Brute Force & Ignorance (BFI) demuxer
3 * Copyright (c) 2008 Sisir Koppaka
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/**
23 * @file
24 * @brief Brute Force & Ignorance (.bfi) file demuxer
25 * @author Sisir Koppaka ( sisir.koppaka at gmail dot com )
26 * @see http://wiki.multimedia.cx/index.php?title=BFI
27 */
28
29#include "libavutil/channel_layout.h"
30#include "libavutil/intreadwrite.h"
31#include "avformat.h"
32#include "demux.h"
33#include "internal.h"
34
35typedef struct BFIContext {
36    int nframes;
37    int audio_frame;
38    int video_frame;
39    int video_size;
40    int avflag;
41} BFIContext;
42
43static int bfi_probe(const AVProbeData * p)
44{
45    /* Check file header */
46    if (AV_RL32(p->buf) == MKTAG('B', 'F', '&', 'I'))
47        return AVPROBE_SCORE_MAX;
48    else
49        return 0;
50}
51
52static int bfi_read_header(AVFormatContext * s)
53{
54    BFIContext *bfi = s->priv_data;
55    AVIOContext *pb = s->pb;
56    AVStream *vstream;
57    AVStream *astream;
58    int ret, fps, chunk_header;
59
60    /* Initialize the video codec... */
61    vstream = avformat_new_stream(s, NULL);
62    if (!vstream)
63        return AVERROR(ENOMEM);
64
65    /* Initialize the audio codec... */
66    astream = avformat_new_stream(s, NULL);
67    if (!astream)
68        return AVERROR(ENOMEM);
69
70    /* Set the total number of frames. */
71    avio_skip(pb, 8);
72    chunk_header           = avio_rl32(pb);
73    if (chunk_header < 3)
74        return AVERROR_INVALIDDATA;
75
76    bfi->nframes           = avio_rl32(pb);
77    if (bfi->nframes < 0)
78        return AVERROR_INVALIDDATA;
79    avio_rl32(pb);
80    avio_rl32(pb);
81    avio_rl32(pb);
82    fps                    = avio_rl32(pb);
83    avio_skip(pb, 12);
84    vstream->codecpar->width  = avio_rl32(pb);
85    vstream->codecpar->height = avio_rl32(pb);
86
87    /*Load the palette to extradata */
88    avio_skip(pb, 8);
89    ret = ff_get_extradata(s, vstream->codecpar, pb, 768);
90    if (ret < 0)
91        return ret;
92
93    astream->codecpar->sample_rate = avio_rl32(pb);
94    if (astream->codecpar->sample_rate <= 0) {
95        av_log(s, AV_LOG_ERROR, "Invalid sample rate %d\n", astream->codecpar->sample_rate);
96        return AVERROR_INVALIDDATA;
97    }
98
99    /* Set up the video codec... */
100    avpriv_set_pts_info(vstream, 32, 1, fps);
101    vstream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
102    vstream->codecpar->codec_id   = AV_CODEC_ID_BFI;
103    vstream->codecpar->format     = AV_PIX_FMT_PAL8;
104    vstream->nb_frames            =
105    vstream->duration             = bfi->nframes;
106
107    /* Set up the audio codec now... */
108    astream->codecpar->codec_type      = AVMEDIA_TYPE_AUDIO;
109    astream->codecpar->codec_id        = AV_CODEC_ID_PCM_U8;
110    astream->codecpar->ch_layout       = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
111    astream->codecpar->bits_per_coded_sample = 8;
112    astream->codecpar->bit_rate        =
113        (int64_t)astream->codecpar->sample_rate * astream->codecpar->bits_per_coded_sample;
114    avio_seek(pb, chunk_header - 3, SEEK_SET);
115    avpriv_set_pts_info(astream, 64, 1, astream->codecpar->sample_rate);
116    return 0;
117}
118
119
120static int bfi_read_packet(AVFormatContext * s, AVPacket * pkt)
121{
122    BFIContext *bfi = s->priv_data;
123    AVIOContext *pb = s->pb;
124    int ret, audio_offset, video_offset, chunk_size, audio_size = 0;
125    if (bfi->nframes == 0 || avio_feof(pb)) {
126        return AVERROR_EOF;
127    }
128
129    /* If all previous chunks were completely read, then find a new one... */
130    if (!bfi->avflag) {
131        uint32_t state = 0;
132        while(state != MKTAG('S','A','V','I')){
133            if (avio_feof(pb))
134                return AVERROR(EIO);
135            state = 256*state + avio_r8(pb);
136        }
137        /* Now that the chunk's location is confirmed, we proceed... */
138        chunk_size      = avio_rl32(pb);
139        avio_rl32(pb);
140        audio_offset    = avio_rl32(pb);
141        avio_rl32(pb);
142        video_offset    = avio_rl32(pb);
143        if (audio_offset < 0 || video_offset < audio_offset || chunk_size < video_offset) {
144            av_log(s, AV_LOG_ERROR, "Invalid audio/video offsets or chunk size\n");
145            return AVERROR_INVALIDDATA;
146        }
147        audio_size      = video_offset - audio_offset;
148        bfi->video_size = chunk_size - video_offset;
149
150        //Tossing an audio packet at the audio decoder.
151        ret = av_get_packet(pb, pkt, audio_size);
152        if (ret < 0)
153            return ret;
154
155        pkt->pts          = bfi->audio_frame;
156        bfi->audio_frame += ret;
157    } else if (bfi->video_size > 0) {
158
159        //Tossing a video packet at the video decoder.
160        ret = av_get_packet(pb, pkt, bfi->video_size);
161        if (ret < 0)
162            return ret;
163
164        pkt->pts          = bfi->video_frame;
165        bfi->video_frame += ret / bfi->video_size;
166
167        /* One less frame to read. A cursory decrement. */
168        bfi->nframes--;
169    } else {
170        /* Empty video packet */
171        ret = AVERROR(EAGAIN);
172    }
173
174    bfi->avflag       = !bfi->avflag;
175    pkt->stream_index = bfi->avflag;
176    return ret;
177}
178
179const AVInputFormat ff_bfi_demuxer = {
180    .name           = "bfi",
181    .long_name      = NULL_IF_CONFIG_SMALL("Brute Force & Ignorance"),
182    .priv_data_size = sizeof(BFIContext),
183    .read_probe     = bfi_probe,
184    .read_header    = bfi_read_header,
185    .read_packet    = bfi_read_packet,
186};
187