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