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