Browse code

Bethsoft VID demuxer and video decoder patch by Nicholas Tung, ntung ntung com

Originally committed as revision 8649 to svn://svn.ffmpeg.org/ffmpeg/trunk

Nicholas Tung authored on 2007/04/08 05:51:58
Showing 11 changed files
... ...
@@ -78,6 +78,7 @@ version <next>
78 78
 - Gamecube movie (.THP) playback system
79 79
 - Blackfin optimizations
80 80
 - Interplay C93 demuxer and video decoder
81
+- Bethsoft VID demuxer and video decoder
81 82
 
82 83
 version 0.4.9-pre1:
83 84
 
... ...
@@ -905,6 +905,8 @@ different game cutscenes repacked for use with ScummVM.
905 905
 @tab Used on the Nintendo GameCube.
906 906
 @item C93 @tab    @tab X
907 907
 @tab Used in the game Cyberia from Interplay.
908
+@item Bethsoft VID @tab    @tab X
909
+@tab Used in some games from Bethesda Softworks.
908 910
 @end multitable
909 911
 
910 912
 @code{X} means that encoding (resp. decoding) is supported.
... ...
@@ -1015,6 +1017,7 @@ following image formats are supported:
1015 1015
 @item DXA Video              @tab     @tab  X @tab Codec originally used in Feeble Files game.
1016 1016
 @item AVID DNxHD             @tab     @tab  X @tab aka SMPTE VC3
1017 1017
 @item C93 Video              @tab     @tab  X @tab Codec used in Cyberia game.
1018
+@item Bethsoft VID           @tab     @tab  X @tab Used in some games from Bethesda Softworks.
1018 1019
 @end multitable
1019 1020
 
1020 1021
 @code{X} means that encoding (resp. decoding) is supported.
... ...
@@ -54,6 +54,7 @@ OBJS-$(CONFIG_ASV1_ENCODER)            += asv1.o
54 54
 OBJS-$(CONFIG_ASV2_DECODER)            += asv1.o
55 55
 OBJS-$(CONFIG_ASV2_ENCODER)            += asv1.o
56 56
 OBJS-$(CONFIG_AVS_DECODER)             += avs.o
57
+OBJS-$(CONFIG_BETHSOFTVID_DECODER)     += bethsoftvideo.o
57 58
 OBJS-$(CONFIG_BMP_DECODER)             += bmp.o
58 59
 OBJS-$(CONFIG_BMP_ENCODER)             += bmpenc.o
59 60
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
... ...
@@ -58,6 +58,7 @@ void avcodec_register_all(void)
58 58
     REGISTER_ENCDEC (ASV1, asv1);
59 59
     REGISTER_ENCDEC (ASV2, asv2);
60 60
     REGISTER_DECODER(AVS, avs);
61
+    REGISTER_DECODER(BETHSOFTVID, bethsoftvid);
61 62
     REGISTER_ENCDEC (BMP, bmp);
62 63
     REGISTER_DECODER(C93, c93);
63 64
     REGISTER_DECODER(CAVS, cavs);
... ...
@@ -161,6 +161,7 @@ enum CodecID {
161 161
     CODEC_ID_THP,
162 162
     CODEC_ID_SGI,
163 163
     CODEC_ID_C93,
164
+    CODEC_ID_BETHSOFTVID,
164 165
 
165 166
     /* various PCM "codecs" */
166 167
     CODEC_ID_PCM_S16LE= 0x10000,
... ...
@@ -2252,6 +2253,7 @@ extern AVCodec amr_wb_decoder;
2252 2252
 extern AVCodec asv1_decoder;
2253 2253
 extern AVCodec asv2_decoder;
2254 2254
 extern AVCodec avs_decoder;
2255
+extern AVCodec bethsoftvid_decoder;
2255 2256
 extern AVCodec bmp_decoder;
2256 2257
 extern AVCodec c93_decoder;
2257 2258
 extern AVCodec cavs_decoder;
2258 2259
new file mode 100644
... ...
@@ -0,0 +1,139 @@
0
+/*
1
+ * Bethesda VID video decoder
2
+ * Copyright (C) 2007 Nicholas Tung
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+/**
22
+ * @file bethsoftvideo.c
23
+ * @brief Bethesda Softworks VID Video Decoder
24
+ * @author Nicholas Tung [ntung (at. ntung com] (2007-03)
25
+ * @sa http://wiki.multimedia.cx/index.php?title=Bethsoft_VID
26
+ * @sa http://www.svatopluk.com/andux/docs/dfvid.html
27
+ */
28
+
29
+#include "common.h"
30
+#include "dsputil.h"
31
+#include "bethsoftvideo.h"
32
+#include "bytestream.h"
33
+
34
+typedef struct BethsoftvidContext {
35
+    AVFrame frame;
36
+} BethsoftvidContext;
37
+
38
+static int bethsoftvid_decode_init(AVCodecContext *avctx)
39
+{
40
+    BethsoftvidContext *vid = avctx->priv_data;
41
+    vid->frame.reference = 1;
42
+    vid->frame.buffer_hints = FF_BUFFER_HINTS_VALID |
43
+        FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE;
44
+    avctx->pix_fmt = PIX_FMT_PAL8;    // palette in vid->frame.data[1]
45
+    av_log(avctx, AV_LOG_DEBUG, "[bethsoftvid video decoder] init\n");
46
+    return 0;
47
+}
48
+
49
+static void set_palette(AVFrame * frame, uint8_t * palette_buffer)
50
+{
51
+    uint32_t * palette = (uint32_t *)frame->data[1];
52
+    int a;
53
+    for(a = 0; a < VID_PALETTE_NUMCOLORS; a++)
54
+    {
55
+        palette[a] = AV_RB24(&palette_buffer[a * 3]) * 4;    // multiply all colors by 4
56
+    }
57
+    frame->palette_has_changed = 1;
58
+}
59
+
60
+static int bethsoftvid_decode_frame(AVCodecContext *avctx,
61
+                              void *data, int *data_size,
62
+                              uint8_t *buf, int buf_size)
63
+{
64
+    BethsoftvidContext * vid = avctx->priv_data;
65
+    char block_type;
66
+    uint8_t * destination;
67
+    uint8_t * frame_end;
68
+    int line_remaining = avctx->width;          // number of bytes remaining on a line
69
+    const int wrap_to_next_line = vid->frame.linesize[0] - avctx->width;
70
+    uint8_t rle_num_bytes;
71
+    int yoffset;
72
+
73
+    av_log(avctx, AV_LOG_DEBUG, "[bethsoftvid video decoder] decoding frame\n");
74
+
75
+    // reget buffer will copy old data, good for simple difference frames
76
+    if (avctx->reget_buffer(avctx, &vid->frame)) {
77
+        av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
78
+        return -1;
79
+    }
80
+    destination = vid->frame.data[0];
81
+    frame_end = vid->frame.data[0] + vid->frame.linesize[0] * avctx->height;
82
+
83
+    switch(block_type = *buf++)
84
+    {
85
+        case PALETTE_BLOCK: set_palette(&vid->frame, buf); return 0;
86
+        case VIDEO_YOFFSET_DIFFERENCE_FRAME_BLOCK:
87
+            yoffset = bytestream_get_le16(&buf);
88
+            if(yoffset >= avctx->height) { return -1; }
89
+            destination += vid->frame.linesize[0] * yoffset;
90
+    }
91
+
92
+    // main code
93
+    while((rle_num_bytes = *buf++))
94
+    {
95
+        int length = rle_num_bytes & 0x7f;
96
+
97
+        // copy any bytes starting at the current position, and ending at the frame width
98
+        while(length > line_remaining)
99
+        {
100
+            if(rle_num_bytes < 0x80) { bytestream_get_buffer(&buf, destination, line_remaining); }
101
+            else if(block_type == VIDEO_FULL_FRAME_BLOCK) { memset(destination, buf[0], line_remaining); }
102
+            length -= line_remaining;      // decrement the number of bytes to be copied
103
+            destination += line_remaining + wrap_to_next_line;    // skip over extra bytes at end of frame
104
+            line_remaining = avctx->width;
105
+            if(destination == frame_end) { goto end; }
106
+        }
107
+
108
+        // copy any remaining bytes after / if line overflows
109
+        if(rle_num_bytes < 0x80) { bytestream_get_buffer(&buf, destination, length); }
110
+        else if(block_type == VIDEO_FULL_FRAME_BLOCK) { memset(destination, *buf++, length); }
111
+        line_remaining -= length;
112
+        destination += length;
113
+    }
114
+    end:
115
+
116
+    *data_size = sizeof(AVFrame);
117
+    *(AVFrame*)data = vid->frame;
118
+
119
+    return buf_size;
120
+}
121
+
122
+static int bethsoftvid_decode_end(AVCodecContext *avctx)
123
+{
124
+    BethsoftvidContext * vid = avctx->priv_data;
125
+    av_log(avctx, AV_LOG_DEBUG, "[bethsoftvid video decoder] closing\n");
126
+    if(vid->frame.data[0]) { avctx->release_buffer(avctx, &vid->frame); }
127
+    return 0;
128
+}
129
+
130
+AVCodec bethsoftvid_decoder = {
131
+    .name = "bethsoftvid",
132
+    .type = CODEC_TYPE_VIDEO,
133
+    .id = CODEC_ID_BETHSOFTVID,
134
+    .priv_data_size = sizeof(BethsoftvidContext),
135
+    .init = bethsoftvid_decode_init,
136
+    .close = bethsoftvid_decode_end,
137
+    .decode = bethsoftvid_decode_frame,
138
+};
0 139
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+#define VID_PALETTE_NUMCOLORS 256
1
+
2
+enum BethsoftVidBlockType
3
+{
4
+    PALETTE_BLOCK = 0x02,
5
+    FIRST_AUDIO_BLOCK = 0x7c, AUDIO_BLOCK = 0x7d,
6
+    VIDEO_FULL_FRAME_BLOCK = 0x03, VIDEO_DIFFERENCE_FRAME_BLOCK = 0x01, VIDEO_YOFFSET_DIFFERENCE_FRAME_BLOCK = 0x04,
7
+    FINISHED_BLOCK = 0x14,
8
+};
... ...
@@ -28,6 +28,7 @@ OBJS-$(CONFIG_AVI_DEMUXER)               += avidec.o riff.o
28 28
 OBJS-$(CONFIG_AVI_MUXER)                 += avienc.o riff.o
29 29
 OBJS-$(CONFIG_AVISYNTH)                  += avisynth.o
30 30
 OBJS-$(CONFIG_AVS_DEMUXER)               += avs.o vocdec.o voc.o riff.o
31
+OBJS-$(CONFIG_BETHSOFTVID_DEMUXER)       += bethsoftvid.o
31 32
 OBJS-$(CONFIG_C93_DEMUXER)               += c93.o
32 33
 OBJS-$(CONFIG_CRC_MUXER)                 += crc.o
33 34
 OBJS-$(CONFIG_FRAMECRC_MUXER)            += crc.o
... ...
@@ -58,6 +58,7 @@ void av_register_all(void)
58 58
     av_register_input_format(&avisynth_demuxer);
59 59
 #endif
60 60
     REGISTER_DEMUXER (AVS, avs);
61
+    REGISTER_DEMUXER (BETHSOFTVID, bethsoftvid);
61 62
     REGISTER_DEMUXER (C93, c93);
62 63
     REGISTER_MUXER   (CRC, crc);
63 64
     REGISTER_DEMUXER (DAUD, daud);
... ...
@@ -32,6 +32,7 @@ extern AVInputFormat audio_demuxer;
32 32
 extern AVInputFormat avi_demuxer;
33 33
 extern AVInputFormat avisynth_demuxer;
34 34
 extern AVInputFormat avs_demuxer;
35
+extern AVInputFormat bethsoftvid_demuxer;
35 36
 extern AVInputFormat c93_demuxer;
36 37
 extern AVInputFormat daud_demuxer;
37 38
 extern AVInputFormat dc1394_demuxer;
38 39
new file mode 100644
... ...
@@ -0,0 +1,234 @@
0
+/*
1
+ * Bethsoft VID format Demuxer
2
+ * Copyright (c) 2007 Nicholas Tung
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+/**
22
+ * @file bethsoftvid.c
23
+ * @brief Bethesda Softworks VID (.vid) file demuxer
24
+ * @author Nicholas Tung [ntung (at. ntung com] (2007-03)
25
+ * @sa http://wiki.multimedia.cx/index.php?title=Bethsoft_VID
26
+ * @sa http://www.svatopluk.com/andux/docs/dfvid.html
27
+ */
28
+
29
+#include "avformat.h"
30
+#include "bethsoftvideo.h"
31
+
32
+typedef struct BVID_DemuxContext
33
+{
34
+    int nframes;
35
+    /** delay value between frames, added to individual frame delay.
36
+     * custom units, which will be added to other custom units (~=16ms according
37
+     * to free, unofficial documentation) */
38
+    int bethsoft_global_delay;
39
+
40
+    /** video presentation time stamp.
41
+     * delay = 16 milliseconds * (global_delay + per_frame_delay) */
42
+    int64_t video_pts;
43
+
44
+    int is_finished;
45
+
46
+} BVID_DemuxContext;
47
+
48
+static int vid_probe(AVProbeData *p)
49
+{
50
+    // little endian VID tag, file starts with "VID\0"
51
+    if (p->buf_size < 4 || AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0))
52
+        return 0;
53
+
54
+    return AVPROBE_SCORE_MAX;
55
+}
56
+
57
+static int vid_read_header(AVFormatContext *s,
58
+                            AVFormatParameters *ap)
59
+{
60
+    BVID_DemuxContext *vid = s->priv_data;     // permanent data outside of function
61
+    ByteIOContext *pb = &s->pb;                // io to file
62
+    AVStream *stream;
63
+
64
+    /* load main header. Contents:
65
+    *    bytes: 'V' 'I' 'D'
66
+    *    int16s: always_512, nframes, width, height, delay, always_14
67
+    */
68
+    url_fseek(pb, 5, SEEK_CUR);
69
+    vid->nframes = get_le16(pb);
70
+
71
+    // FFmpeg central code will use this; don't need to return or anything
72
+    // initialize the bethsoft codec
73
+    stream = av_new_stream(s, 0);
74
+    if (!stream) { return AVERROR_NOMEM; }
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);
82
+    get_le16(pb);
83
+
84
+    // done with video codec, set up audio codec
85
+    stream = av_new_stream(s, 0);
86
+    if (!stream) { return AVERROR_NOMEM; }
87
+    stream->codec->codec_type = CODEC_TYPE_AUDIO;
88
+    stream->codec->codec_id = CODEC_ID_PCM_U8;
89
+    stream->codec->channels = 1;
90
+    stream->codec->sample_rate = 11025;
91
+    stream->codec->bits_per_sample = 8;
92
+    stream->codec->bit_rate = stream->codec->channels * stream->codec->sample_rate * stream->codec->bits_per_sample;
93
+
94
+    return 0;
95
+}
96
+
97
+#define BUFFER_PADDING_SIZE 1000
98
+static int read_frame(BVID_DemuxContext *vid, ByteIOContext *pb, AVPacket *pkt,
99
+                      uint8_t block_type, AVFormatContext *s, int npixels)
100
+{
101
+    uint8_t * vidbuf_start = NULL;
102
+    int vidbuf_nbytes = 0;
103
+    int rle_num_bytes;
104
+    int bytes_copied = 0;
105
+    int position;
106
+    size_t vidbuf_capacity;
107
+
108
+    vidbuf_start = av_malloc(vidbuf_capacity = BUFFER_PADDING_SIZE);
109
+    if(!vidbuf_start) { return AVERROR_NOMEM; }
110
+
111
+    // save the file position for the packet, include block type
112
+    position = url_ftell(pb) - 1;
113
+
114
+    // set the block type for the decoder
115
+    vidbuf_start[vidbuf_nbytes++] = block_type;
116
+
117
+    // get the video delay (next int16), and set the presentation time
118
+    vid->video_pts += vid->bethsoft_global_delay + get_le16(pb);
119
+
120
+    // set the y offset if it exists (decoder header data should be in data section)
121
+    if(block_type == VIDEO_YOFFSET_DIFFERENCE_FRAME_BLOCK)
122
+    {
123
+        if(get_buffer(pb, &vidbuf_start[vidbuf_nbytes], 2) != 2) { return AVERROR_IO; }
124
+        vidbuf_nbytes += 2;
125
+    }
126
+
127
+    do
128
+    {
129
+        vidbuf_start = av_fast_realloc(vidbuf_start, &vidbuf_capacity, vidbuf_nbytes + BUFFER_PADDING_SIZE);
130
+        if(!vidbuf_start) { return AVERROR_NOMEM; }
131
+
132
+        rle_num_bytes = get_byte(pb);
133
+        vidbuf_start[vidbuf_nbytes++] = rle_num_bytes;
134
+
135
+        if(rle_num_bytes > 0x80) // rle sequence
136
+        {
137
+            if(block_type == VIDEO_FULL_FRAME_BLOCK) { vidbuf_start[vidbuf_nbytes++] = get_byte(pb); }
138
+            bytes_copied += rle_num_bytes - 0x80;
139
+        }
140
+        else if(rle_num_bytes) // plain sequence
141
+        {
142
+            if(get_buffer(pb, &vidbuf_start[vidbuf_nbytes], rle_num_bytes) != rle_num_bytes)
143
+                return AVERROR_IO;
144
+            vidbuf_nbytes += rle_num_bytes;
145
+            bytes_copied += rle_num_bytes;
146
+        }
147
+        if(bytes_copied == npixels) // sometimes no stop character is given, need to keep track of bytes copied
148
+        {
149
+            // may contain a 0 byte even if read all pixels
150
+            if(get_byte(pb)) { url_fseek(pb, -1, SEEK_CUR); }
151
+            break;
152
+        }
153
+        if(bytes_copied > npixels) { return -1; }    // error
154
+    } while(rle_num_bytes);
155
+
156
+    // copy data into packet
157
+    if(av_new_packet(pkt, vidbuf_nbytes)) { return AVERROR_NOMEM; }
158
+    memcpy(pkt->data, vidbuf_start, vidbuf_nbytes);
159
+    av_free(vidbuf_start);
160
+
161
+    pkt->pos = position;
162
+    pkt->stream_index = 0;  // use the video decoder, which was initialized as the first stream
163
+    pkt->pts = vid->video_pts;
164
+
165
+    vid->nframes--;  // used to check if all the frames were read
166
+    return vidbuf_nbytes;
167
+}
168
+
169
+static int vid_read_packet(AVFormatContext *s,
170
+                           AVPacket *pkt)
171
+{
172
+    BVID_DemuxContext *vid = s->priv_data;     // permanent data outside of function
173
+    ByteIOContext *pb = &s->pb;                // io to file
174
+    unsigned char block_type;                  // block type
175
+    int audio_length;
176
+    int ret_value;
177
+
178
+    av_log(s, AV_LOG_DEBUG, "[bethsoftvid demuxer read_packet]");
179
+
180
+    if(vid->is_finished || url_feof(pb)) { return AVERROR_IO; }
181
+
182
+    block_type = get_byte(pb);
183
+    switch(block_type)
184
+    {
185
+        case PALETTE_BLOCK:
186
+            av_log(s, AV_LOG_DEBUG, "palette block.\n");
187
+            url_fseek(pb, -1, SEEK_CUR);     // include block type
188
+            ret_value = av_get_packet(pb, pkt, 3 * VID_PALETTE_NUMCOLORS + 1);
189
+            if(ret_value != 3 * VID_PALETTE_NUMCOLORS + 1) { av_free_packet(pkt); return AVERROR_IO; }
190
+            pkt->stream_index = 0;
191
+            return ret_value;
192
+
193
+        case FIRST_AUDIO_BLOCK:
194
+            av_log(s, AV_LOG_DEBUG, "first ");
195
+            get_le16(pb); //    some unused constant
196
+            // soundblaster DAC used for sample rate, as on specification page (link above)
197
+            s->streams[1]->codec->sample_rate = 1000000 / (256 - get_byte(pb));
198
+            s->streams[1]->codec->bit_rate = s->streams[1]->codec->channels * s->streams[1]->codec->sample_rate * s->streams[1]->codec->bits_per_sample;
199
+        case AUDIO_BLOCK:
200
+            av_log(s, AV_LOG_DEBUG, "audio block.\n");
201
+            audio_length = get_le16(pb);
202
+            ret_value = av_get_packet(pb, pkt, audio_length);
203
+            pkt->stream_index = 1;
204
+            return (ret_value != audio_length ? AVERROR_IO : ret_value);
205
+
206
+        case VIDEO_DIFFERENCE_FRAME_BLOCK: av_log(s, AV_LOG_DEBUG, "non-");
207
+        case VIDEO_YOFFSET_DIFFERENCE_FRAME_BLOCK: av_log(s, AV_LOG_DEBUG, "offset ");
208
+        case VIDEO_FULL_FRAME_BLOCK: av_log(s, AV_LOG_DEBUG, "video block.\n");
209
+            return read_frame(vid, pb, pkt, block_type, s,
210
+                              s->streams[0]->codec->width * s->streams[0]->codec->height);
211
+
212
+        case FINISHED_BLOCK:
213
+            if(vid->nframes != 0)
214
+                av_log(s, AV_LOG_VERBOSE, "reached terminating character but not all frames read.\n");
215
+            vid->is_finished = 1;
216
+            av_log(s, AV_LOG_DEBUG, "terminating block.\n");
217
+            return AVERROR_IO;
218
+        default:
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;
221
+    }
222
+
223
+    return 0;
224
+}
225
+
226
+AVInputFormat bethsoftvid_demuxer = {
227
+    "bethsoftvid",
228
+    "Bethesda Softworks 'Daggerfall' VID format",
229
+    sizeof(BVID_DemuxContext),
230
+    vid_probe,
231
+    vid_read_header,
232
+    vid_read_packet,
233
+};