2 * Bethsoft VID format Demuxer
3 * Copyright (c) 2007 Nicholas Tung
5 * This file is part of FFmpeg.
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.
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.
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
23 * @file libavformat/bethsoftvid.c
24 * @brief Bethesda Softworks VID (.vid) file demuxer
25 * @author Nicholas Tung [ntung (at. ntung com] (2007-03)
26 * @sa http://wiki.multimedia.cx/index.php?title=Bethsoft_VID
27 * @sa http://www.svatopluk.com/andux/docs/dfvid.html
30 #include "libavutil/intreadwrite.h"
32 #include "libavcodec/bethsoftvideo.h"
34 typedef struct BVID_DemuxContext
37 /** delay value between frames, added to individual frame delay.
38 * custom units, which will be added to other custom units (~=16ms according
39 * to free, unofficial documentation) */
40 int bethsoft_global_delay
;
42 /** video presentation time stamp.
43 * delay = 16 milliseconds * (global_delay + per_frame_delay) */
50 static int vid_probe(AVProbeData
*p
)
52 // little endian VID tag, file starts with "VID\0"
53 if (AV_RL32(p
->buf
) != MKTAG('V', 'I', 'D', 0))
56 return AVPROBE_SCORE_MAX
;
59 static int vid_read_header(AVFormatContext
*s
,
60 AVFormatParameters
*ap
)
62 BVID_DemuxContext
*vid
= s
->priv_data
;
63 ByteIOContext
*pb
= s
->pb
;
66 /* load main header. Contents:
68 * int16s: always_512, nframes, width, height, delay, always_14
70 url_fseek(pb
, 5, SEEK_CUR
);
71 vid
->nframes
= get_le16(pb
);
73 stream
= av_new_stream(s
, 0);
75 return AVERROR(ENOMEM
);
76 av_set_pts_info(stream
, 32, 1, 60); // 16 ms increments, i.e. 60 fps
77 stream
->codec
->codec_type
= CODEC_TYPE_VIDEO
;
78 stream
->codec
->codec_id
= CODEC_ID_BETHSOFTVID
;
79 stream
->codec
->width
= get_le16(pb
);
80 stream
->codec
->height
= get_le16(pb
);
81 stream
->codec
->pix_fmt
= PIX_FMT_PAL8
;
82 vid
->bethsoft_global_delay
= get_le16(pb
);
85 // done with video codec, set up audio codec
86 stream
= av_new_stream(s
, 0);
88 return AVERROR(ENOMEM
);
89 stream
->codec
->codec_type
= CODEC_TYPE_AUDIO
;
90 stream
->codec
->codec_id
= CODEC_ID_PCM_U8
;
91 stream
->codec
->channels
= 1;
92 stream
->codec
->sample_rate
= 11025;
93 stream
->codec
->bits_per_coded_sample
= 8;
94 stream
->codec
->bit_rate
= stream
->codec
->channels
* stream
->codec
->sample_rate
* stream
->codec
->bits_per_coded_sample
;
99 #define BUFFER_PADDING_SIZE 1000
100 static int read_frame(BVID_DemuxContext
*vid
, ByteIOContext
*pb
, AVPacket
*pkt
,
101 uint8_t block_type
, AVFormatContext
*s
, int npixels
)
103 uint8_t * vidbuf_start
= NULL
;
104 int vidbuf_nbytes
= 0;
106 int bytes_copied
= 0;
108 unsigned int vidbuf_capacity
;
110 vidbuf_start
= av_malloc(vidbuf_capacity
= BUFFER_PADDING_SIZE
);
112 return AVERROR(ENOMEM
);
114 // save the file position for the packet, include block type
115 position
= url_ftell(pb
) - 1;
117 vidbuf_start
[vidbuf_nbytes
++] = block_type
;
119 // get the video delay (next int16), and set the presentation time
120 vid
->video_pts
+= vid
->bethsoft_global_delay
+ get_le16(pb
);
122 // set the y offset if it exists (decoder header data should be in data section)
123 if(block_type
== VIDEO_YOFF_P_FRAME
){
124 if(get_buffer(pb
, &vidbuf_start
[vidbuf_nbytes
], 2) != 2)
130 vidbuf_start
= av_fast_realloc(vidbuf_start
, &vidbuf_capacity
, vidbuf_nbytes
+ BUFFER_PADDING_SIZE
);
132 return AVERROR(ENOMEM
);
135 vidbuf_start
[vidbuf_nbytes
++] = code
;
137 if(code
>= 0x80){ // rle sequence
138 if(block_type
== VIDEO_I_FRAME
)
139 vidbuf_start
[vidbuf_nbytes
++] = get_byte(pb
);
140 } else if(code
){ // plain sequence
141 if(get_buffer(pb
, &vidbuf_start
[vidbuf_nbytes
], code
) != code
)
143 vidbuf_nbytes
+= code
;
145 bytes_copied
+= code
& 0x7F;
146 if(bytes_copied
== npixels
){ // sometimes no stop character is given, need to keep track of bytes copied
147 // may contain a 0 byte even if read all pixels
149 url_fseek(pb
, -1, SEEK_CUR
);
152 if(bytes_copied
> npixels
)
156 // copy data into packet
157 if(av_new_packet(pkt
, vidbuf_nbytes
) < 0)
159 memcpy(pkt
->data
, vidbuf_start
, vidbuf_nbytes
);
160 av_free(vidbuf_start
);
163 pkt
->stream_index
= 0; // use the video decoder, which was initialized as the first stream
164 pkt
->pts
= vid
->video_pts
;
166 vid
->nframes
--; // used to check if all the frames were read
167 return vidbuf_nbytes
;
169 av_free(vidbuf_start
);
173 static int vid_read_packet(AVFormatContext
*s
,
176 BVID_DemuxContext
*vid
= s
->priv_data
;
177 ByteIOContext
*pb
= s
->pb
;
178 unsigned char block_type
;
182 if(vid
->is_finished
|| url_feof(pb
))
185 block_type
= get_byte(pb
);
188 url_fseek(pb
, -1, SEEK_CUR
); // include block type
189 ret_value
= av_get_packet(pb
, pkt
, 3 * 256 + 1);
190 if(ret_value
!= 3 * 256 + 1){
194 pkt
->stream_index
= 0;
197 case FIRST_AUDIO_BLOCK
:
199 // soundblaster DAC used for sample rate, as on specification page (link above)
200 s
->streams
[1]->codec
->sample_rate
= 1000000 / (256 - get_byte(pb
));
201 s
->streams
[1]->codec
->bit_rate
= s
->streams
[1]->codec
->channels
* s
->streams
[1]->codec
->sample_rate
* s
->streams
[1]->codec
->bits_per_coded_sample
;
203 audio_length
= get_le16(pb
);
204 ret_value
= av_get_packet(pb
, pkt
, audio_length
);
205 pkt
->stream_index
= 1;
206 return ret_value
!= audio_length
? AVERROR(EIO
) : ret_value
;
209 case VIDEO_YOFF_P_FRAME
:
211 return read_frame(vid
, pb
, pkt
, block_type
, s
,
212 s
->streams
[0]->codec
->width
* s
->streams
[0]->codec
->height
);
215 if(vid
->nframes
!= 0)
216 av_log(s
, AV_LOG_VERBOSE
, "reached terminating character but not all frames read.\n");
217 vid
->is_finished
= 1;
220 av_log(s
, AV_LOG_ERROR
, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n",
221 block_type
, block_type
, block_type
); return -1;
227 AVInputFormat bethsoftvid_demuxer
= {
229 NULL_IF_CONFIG_SMALL("Bethesda Softworks VID format"),
230 sizeof(BVID_DemuxContext
),