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