/src/ffmpeg/libavformat/bethsoftvid.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Bethsoft VID format Demuxer |
3 | | * Copyright (c) 2007 Nicholas Tung |
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 Bethesda Softworks VID (.vid) file demuxer |
25 | | * @author Nicholas Tung [ntung (at. ntung com] (2007-03) |
26 | | * @see http://wiki.multimedia.cx/index.php?title=Bethsoft_VID |
27 | | * @see http://www.svatopluk.com/andux/docs/dfvid.html |
28 | | */ |
29 | | |
30 | | #include "libavutil/channel_layout.h" |
31 | | #include "libavutil/imgutils.h" |
32 | | #include "libavutil/intreadwrite.h" |
33 | | #include "libavutil/mem.h" |
34 | | #include "avformat.h" |
35 | | #include "demux.h" |
36 | | #include "internal.h" |
37 | | #include "libavcodec/bethsoftvideo.h" |
38 | | |
39 | 5.43k | #define BVID_PALETTE_SIZE 3 * 256 |
40 | | |
41 | 1.85k | #define DEFAULT_SAMPLE_RATE 11111 |
42 | | |
43 | | typedef struct BVID_DemuxContext |
44 | | { |
45 | | int nframes; |
46 | | int sample_rate; /**< audio sample rate */ |
47 | | int width; /**< video width */ |
48 | | int height; /**< video height */ |
49 | | /** delay value between frames, added to individual frame delay. |
50 | | * custom units, which will be added to other custom units (~=16ms according |
51 | | * to free, unofficial documentation) */ |
52 | | int bethsoft_global_delay; |
53 | | int video_index; /**< video stream index */ |
54 | | int audio_index; /**< audio stream index */ |
55 | | int has_palette; |
56 | | uint8_t palette[BVID_PALETTE_SIZE]; |
57 | | |
58 | | int is_finished; |
59 | | |
60 | | } BVID_DemuxContext; |
61 | | |
62 | | static int vid_probe(const AVProbeData *p) |
63 | 358k | { |
64 | | // little-endian VID tag, file starts with "VID\0" |
65 | 358k | if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0)) |
66 | 358k | return 0; |
67 | | |
68 | 106 | if (p->buf[4] != 2) |
69 | 73 | return AVPROBE_SCORE_MAX / 4; |
70 | | |
71 | 33 | return AVPROBE_SCORE_MAX; |
72 | 106 | } |
73 | | |
74 | | static int vid_read_header(AVFormatContext *s) |
75 | 6.18k | { |
76 | 6.18k | BVID_DemuxContext *vid = s->priv_data; |
77 | 6.18k | AVIOContext *pb = s->pb; |
78 | 6.18k | int ret; |
79 | | |
80 | | /* load main header. Contents: |
81 | | * bytes: 'V' 'I' 'D' |
82 | | * int16s: always_512, nframes, width, height, delay, always_14 |
83 | | */ |
84 | 6.18k | avio_skip(pb, 5); |
85 | 6.18k | vid->nframes = avio_rl16(pb); |
86 | 6.18k | vid->width = avio_rl16(pb); |
87 | 6.18k | vid->height = avio_rl16(pb); |
88 | 6.18k | vid->bethsoft_global_delay = avio_rl16(pb); |
89 | 6.18k | avio_rl16(pb); |
90 | | |
91 | 6.18k | ret = av_image_check_size(vid->width, vid->height, 0, s); |
92 | 6.18k | if (ret < 0) |
93 | 4.33k | return ret; |
94 | | |
95 | | // wait until the first packet to create each stream |
96 | 1.85k | vid->video_index = -1; |
97 | 1.85k | vid->audio_index = -1; |
98 | 1.85k | vid->sample_rate = DEFAULT_SAMPLE_RATE; |
99 | 1.85k | s->ctx_flags |= AVFMTCTX_NOHEADER; |
100 | | |
101 | 1.85k | return 0; |
102 | 6.18k | } |
103 | | |
104 | 3.98M | #define BUFFER_PADDING_SIZE 1000 |
105 | | static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt, |
106 | | uint8_t block_type, AVFormatContext *s) |
107 | 190k | { |
108 | 190k | uint8_t * vidbuf_start = NULL; |
109 | 190k | int vidbuf_nbytes = 0; |
110 | 190k | int code; |
111 | 190k | int bytes_copied = 0; |
112 | 190k | int position, duration, npixels; |
113 | 190k | unsigned int vidbuf_capacity; |
114 | 190k | int ret = 0; |
115 | 190k | AVStream *st; |
116 | | |
117 | 190k | if (vid->video_index < 0) { |
118 | 1.21k | st = avformat_new_stream(s, NULL); |
119 | 1.21k | if (!st) |
120 | 0 | return AVERROR(ENOMEM); |
121 | 1.21k | vid->video_index = st->index; |
122 | 1.21k | if (vid->audio_index < 0) { |
123 | 632 | avpriv_request_sample(s, "Using default video time base since " |
124 | 632 | "having no audio packet before the first " |
125 | 632 | "video packet"); |
126 | 632 | } |
127 | 1.21k | avpriv_set_pts_info(st, 64, 185, vid->sample_rate); |
128 | 1.21k | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
129 | 1.21k | st->codecpar->codec_id = AV_CODEC_ID_BETHSOFTVID; |
130 | 1.21k | st->codecpar->width = vid->width; |
131 | 1.21k | st->codecpar->height = vid->height; |
132 | 1.21k | } |
133 | 190k | st = s->streams[vid->video_index]; |
134 | 190k | npixels = st->codecpar->width * st->codecpar->height; |
135 | | |
136 | 190k | vidbuf_start = av_malloc(vidbuf_capacity = BUFFER_PADDING_SIZE); |
137 | 190k | if(!vidbuf_start) |
138 | 0 | return AVERROR(ENOMEM); |
139 | | |
140 | | // save the file position for the packet, include block type |
141 | 190k | position = avio_tell(pb) - 1; |
142 | | |
143 | 190k | vidbuf_start[vidbuf_nbytes++] = block_type; |
144 | | |
145 | | // get the current packet duration |
146 | 190k | duration = vid->bethsoft_global_delay + avio_rl16(pb); |
147 | | |
148 | | // set the y offset if it exists (decoder header data should be in data section) |
149 | 190k | if(block_type == VIDEO_YOFF_P_FRAME){ |
150 | 619 | if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], 2) != 2) { |
151 | 24 | ret = AVERROR(EIO); |
152 | 24 | goto fail; |
153 | 24 | } |
154 | 595 | vidbuf_nbytes += 2; |
155 | 595 | } |
156 | | |
157 | 3.79M | do{ |
158 | 3.79M | uint8_t *tmp = av_fast_realloc(vidbuf_start, &vidbuf_capacity, |
159 | 3.79M | vidbuf_nbytes + BUFFER_PADDING_SIZE); |
160 | 3.79M | if (!tmp) { |
161 | 0 | ret = AVERROR(ENOMEM); |
162 | 0 | goto fail; |
163 | 0 | } |
164 | 3.79M | vidbuf_start = tmp; |
165 | | |
166 | 3.79M | code = avio_r8(pb); |
167 | 3.79M | vidbuf_start[vidbuf_nbytes++] = code; |
168 | | |
169 | 3.79M | if(code >= 0x80){ // rle sequence |
170 | 3.44M | if(block_type == VIDEO_I_FRAME) |
171 | 2.77M | vidbuf_start[vidbuf_nbytes++] = avio_r8(pb); |
172 | 3.44M | } else if(code){ // plain sequence |
173 | 163k | if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], code) != code) { |
174 | 42 | ret = AVERROR(EIO); |
175 | 42 | goto fail; |
176 | 42 | } |
177 | 163k | vidbuf_nbytes += code; |
178 | 163k | } |
179 | 3.79M | bytes_copied += code & 0x7F; |
180 | 3.79M | if(bytes_copied == npixels){ // sometimes no stop character is given, need to keep track of bytes copied |
181 | | // may contain a 0 byte even if read all pixels |
182 | 1.36k | if(avio_r8(pb)) |
183 | 1.16k | avio_seek(pb, -1, SEEK_CUR); |
184 | 1.36k | break; |
185 | 1.36k | } |
186 | 3.79M | if (bytes_copied > npixels) { |
187 | 52 | ret = AVERROR_INVALIDDATA; |
188 | 52 | goto fail; |
189 | 52 | } |
190 | 3.79M | } while(code); |
191 | | |
192 | | // copy data into packet |
193 | 190k | if ((ret = av_new_packet(pkt, vidbuf_nbytes)) < 0) |
194 | 0 | goto fail; |
195 | 190k | memcpy(pkt->data, vidbuf_start, vidbuf_nbytes); |
196 | | |
197 | 190k | pkt->pos = position; |
198 | 190k | pkt->stream_index = vid->video_index; |
199 | 190k | pkt->duration = duration; |
200 | 190k | if (block_type == VIDEO_I_FRAME) |
201 | 160k | pkt->flags |= AV_PKT_FLAG_KEY; |
202 | | |
203 | | /* if there is a new palette available, add it to packet side data */ |
204 | 190k | if (vid->has_palette) { |
205 | 463 | uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, |
206 | 463 | BVID_PALETTE_SIZE); |
207 | 463 | if (!pdata) { |
208 | 0 | ret = AVERROR(ENOMEM); |
209 | 0 | av_log(s, AV_LOG_ERROR, "Failed to allocate palette side data\n"); |
210 | 0 | goto fail; |
211 | 0 | } |
212 | 463 | memcpy(pdata, vid->palette, BVID_PALETTE_SIZE); |
213 | 463 | vid->has_palette = 0; |
214 | 463 | } |
215 | | |
216 | 190k | vid->nframes--; // used to check if all the frames were read |
217 | 190k | fail: |
218 | 190k | av_free(vidbuf_start); |
219 | 190k | return ret; |
220 | 190k | } |
221 | | |
222 | | static int vid_read_packet(AVFormatContext *s, |
223 | | AVPacket *pkt) |
224 | 1.75M | { |
225 | 1.75M | BVID_DemuxContext *vid = s->priv_data; |
226 | 1.75M | AVIOContext *pb = s->pb; |
227 | 1.75M | unsigned char block_type; |
228 | 1.75M | int audio_length; |
229 | 1.75M | int ret_value; |
230 | | |
231 | 1.75M | if(vid->is_finished || avio_feof(pb)) |
232 | 2.00k | return AVERROR_EOF; |
233 | | |
234 | 1.75M | block_type = avio_r8(pb); |
235 | 1.75M | switch(block_type){ |
236 | 2.25k | case PALETTE_BLOCK: |
237 | 2.25k | if (vid->has_palette) { |
238 | 1.75k | av_log(s, AV_LOG_WARNING, "discarding unused palette\n"); |
239 | 1.75k | vid->has_palette = 0; |
240 | 1.75k | } |
241 | 2.25k | if (avio_read(pb, vid->palette, BVID_PALETTE_SIZE) != BVID_PALETTE_SIZE) { |
242 | 24 | return AVERROR(EIO); |
243 | 24 | } |
244 | 2.23k | vid->has_palette = 1; |
245 | 2.23k | return vid_read_packet(s, pkt); |
246 | | |
247 | 868 | case FIRST_AUDIO_BLOCK: |
248 | 868 | avio_rl16(pb); |
249 | | // soundblaster DAC used for sample rate, as on specification page (link above) |
250 | 868 | vid->sample_rate = 1000000 / (256 - avio_r8(pb)); |
251 | 1.55M | case AUDIO_BLOCK: |
252 | 1.55M | if (vid->audio_index < 0) { |
253 | 1.08k | AVStream *st = avformat_new_stream(s, NULL); |
254 | 1.08k | if (!st) |
255 | 0 | return AVERROR(ENOMEM); |
256 | 1.08k | vid->audio_index = st->index; |
257 | 1.08k | st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; |
258 | 1.08k | st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; |
259 | 1.08k | st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; |
260 | 1.08k | st->codecpar->bits_per_coded_sample = 8; |
261 | 1.08k | st->codecpar->sample_rate = vid->sample_rate; |
262 | 1.08k | st->codecpar->bit_rate = 8 * st->codecpar->sample_rate; |
263 | 1.08k | st->start_time = 0; |
264 | 1.08k | avpriv_set_pts_info(st, 64, 1, vid->sample_rate); |
265 | 1.08k | } |
266 | 1.55M | audio_length = avio_rl16(pb); |
267 | 1.55M | if ((ret_value = av_get_packet(pb, pkt, audio_length)) != audio_length) { |
268 | 170 | if (ret_value < 0) |
269 | 101 | return ret_value; |
270 | 69 | av_log(s, AV_LOG_ERROR, "incomplete audio block\n"); |
271 | 69 | return AVERROR(EIO); |
272 | 170 | } |
273 | 1.55M | pkt->stream_index = vid->audio_index; |
274 | 1.55M | pkt->duration = audio_length; |
275 | 1.55M | pkt->flags |= AV_PKT_FLAG_KEY; |
276 | 1.55M | return 0; |
277 | | |
278 | 29.9k | case VIDEO_P_FRAME: |
279 | 30.5k | case VIDEO_YOFF_P_FRAME: |
280 | 190k | case VIDEO_I_FRAME: |
281 | 190k | return read_frame(vid, pb, pkt, block_type, s); |
282 | | |
283 | 19 | case EOF_BLOCK: |
284 | 19 | if(vid->nframes != 0) |
285 | 16 | av_log(s, AV_LOG_VERBOSE, "reached terminating character but not all frames read.\n"); |
286 | 19 | vid->is_finished = 1; |
287 | 19 | return AVERROR(EIO); |
288 | 1.17k | default: |
289 | 1.17k | av_log(s, AV_LOG_ERROR, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n", |
290 | 1.17k | block_type, block_type, block_type); |
291 | 1.17k | return AVERROR_INVALIDDATA; |
292 | 1.75M | } |
293 | 1.75M | } |
294 | | |
295 | | const FFInputFormat ff_bethsoftvid_demuxer = { |
296 | | .p.name = "bethsoftvid", |
297 | | .p.long_name = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"), |
298 | | .priv_data_size = sizeof(BVID_DemuxContext), |
299 | | .read_probe = vid_probe, |
300 | | .read_header = vid_read_header, |
301 | | .read_packet = vid_read_packet, |
302 | | }; |