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
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
31 #include "bethsoftvideo.h"
33 typedef struct BVID_DemuxContext
36 /** delay value between frames, added to individual frame delay.
37 * custom units, which will be added to other custom units (~=16ms according
38 * to free, unofficial documentation) */
39 int bethsoft_global_delay
;
41 /** video presentation time stamp.
42 * delay = 16 milliseconds * (global_delay + per_frame_delay) */
49 static int vid_probe(AVProbeData
*p
)
51 // little endian VID tag, file starts with "VID\0"
52 if (AV_RL32(p
->buf
) != MKTAG('V', 'I', 'D', 0))
55 return AVPROBE_SCORE_MAX
;
58 static int vid_read_header(AVFormatContext
*s
,
59 AVFormatParameters
*ap
)
61 BVID_DemuxContext
*vid
= s
->priv_data
;
62 ByteIOContext
*pb
= s
->pb
;
65 /* load main header. Contents:
67 * int16s: always_512, nframes, width, height, delay, always_14
69 url_fseek(pb
, 5, SEEK_CUR
);
70 vid
->nframes
= get_le16(pb
);
72 stream
= av_new_stream(s
, 0);
74 return AVERROR(ENOMEM
);
75 av_set_pts_info(stream
, 32, 1, 60); // 16 ms increments, i.e. 60 fps
76 stream
->codec
->codec_type
= CODEC_TYPE_VIDEO
;
77 stream
->codec
->codec_id
= CODEC_ID_BETHSOFTVID
;
78 stream
->codec
->width
= get_le16(pb
);
79 stream
->codec
->height
= get_le16(pb
);
80 stream
->codec
->pix_fmt
= PIX_FMT_PAL8
;
81 vid
->bethsoft_global_delay
= get_le16(pb
);
84 // done with video codec, set up audio codec
85 stream
= av_new_stream(s
, 0);
87 return AVERROR(ENOMEM
);
88 stream
->codec
->codec_type
= CODEC_TYPE_AUDIO
;
89 stream
->codec
->codec_id
= CODEC_ID_PCM_U8
;
90 stream
->codec
->channels
= 1;
91 stream
->codec
->sample_rate
= 11025;
92 stream
->codec
->bits_per_sample
= 8;
93 stream
->codec
->bit_rate
= stream
->codec
->channels
* stream
->codec
->sample_rate
* stream
->codec
->bits_per_sample
;
98 #define BUFFER_PADDING_SIZE 1000
99 static int read_frame(BVID_DemuxContext
*vid
, ByteIOContext
*pb
, AVPacket
*pkt
,
100 uint8_t block_type
, AVFormatContext
*s
, int npixels
)
102 uint8_t * vidbuf_start
= NULL
;
103 int vidbuf_nbytes
= 0;
105 int bytes_copied
= 0;
107 unsigned int vidbuf_capacity
;
109 vidbuf_start
= av_malloc(vidbuf_capacity
= BUFFER_PADDING_SIZE
);
111 return AVERROR(ENOMEM
);
113 // save the file position for the packet, include block type
114 position
= url_ftell(pb
) - 1;
116 vidbuf_start
[vidbuf_nbytes
++] = block_type
;
118 // get the video delay (next int16), and set the presentation time
119 vid
->video_pts
+= vid
->bethsoft_global_delay
+ get_le16(pb
);
121 // set the y offset if it exists (decoder header data should be in data section)
122 if(block_type
== VIDEO_YOFF_P_FRAME
){
123 if(get_buffer(pb
, &vidbuf_start
[vidbuf_nbytes
], 2) != 2)
129 vidbuf_start
= av_fast_realloc(vidbuf_start
, &vidbuf_capacity
, vidbuf_nbytes
+ BUFFER_PADDING_SIZE
);
131 return AVERROR(ENOMEM
);
134 vidbuf_start
[vidbuf_nbytes
++] = code
;
136 if(code
>= 0x80){ // rle sequence
137 if(block_type
== VIDEO_I_FRAME
)
138 vidbuf_start
[vidbuf_nbytes
++] = get_byte(pb
);
139 } else if(code
){ // plain sequence
140 if(get_buffer(pb
, &vidbuf_start
[vidbuf_nbytes
], code
) != code
)
142 vidbuf_nbytes
+= code
;
144 bytes_copied
+= code
& 0x7F;
145 if(bytes_copied
== npixels
){ // sometimes no stop character is given, need to keep track of bytes copied
146 // may contain a 0 byte even if read all pixels
148 url_fseek(pb
, -1, SEEK_CUR
);
151 if(bytes_copied
> npixels
)
155 // copy data into packet
156 if(av_new_packet(pkt
, vidbuf_nbytes
) < 0)
158 memcpy(pkt
->data
, vidbuf_start
, vidbuf_nbytes
);
159 av_free(vidbuf_start
);
162 pkt
->stream_index
= 0; // use the video decoder, which was initialized as the first stream
163 pkt
->pts
= vid
->video_pts
;
165 vid
->nframes
--; // used to check if all the frames were read
166 return vidbuf_nbytes
;
168 av_free(vidbuf_start
);
172 static int vid_read_packet(AVFormatContext
*s
,
175 BVID_DemuxContext
*vid
= s
->priv_data
;
176 ByteIOContext
*pb
= s
->pb
;
177 unsigned char block_type
;
181 if(vid
->is_finished
|| url_feof(pb
))
184 block_type
= get_byte(pb
);
187 url_fseek(pb
, -1, SEEK_CUR
); // include block type
188 ret_value
= av_get_packet(pb
, pkt
, 3 * 256 + 1);
189 if(ret_value
!= 3 * 256 + 1){
193 pkt
->stream_index
= 0;
196 case FIRST_AUDIO_BLOCK
:
198 // soundblaster DAC used for sample rate, as on specification page (link above)
199 s
->streams
[1]->codec
->sample_rate
= 1000000 / (256 - get_byte(pb
));
200 s
->streams
[1]->codec
->bit_rate
= s
->streams
[1]->codec
->channels
* s
->streams
[1]->codec
->sample_rate
* s
->streams
[1]->codec
->bits_per_sample
;
202 audio_length
= get_le16(pb
);
203 ret_value
= av_get_packet(pb
, pkt
, audio_length
);
204 pkt
->stream_index
= 1;
205 return (ret_value
!= audio_length
? AVERROR(EIO
) : ret_value
);
208 case VIDEO_YOFF_P_FRAME
:
210 return read_frame(vid
, pb
, pkt
, block_type
, s
,
211 s
->streams
[0]->codec
->width
* s
->streams
[0]->codec
->height
);
214 if(vid
->nframes
!= 0)
215 av_log(s
, AV_LOG_VERBOSE
, "reached terminating character but not all frames read.\n");
216 vid
->is_finished
= 1;
219 av_log(s
, AV_LOG_ERROR
, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n",
220 block_type
, block_type
, block_type
); return -1;
226 AVInputFormat bethsoftvid_demuxer
= {
228 "Bethesda Softworks 'Daggerfall' VID format",
229 sizeof(BVID_DemuxContext
),