xref: /third_party/ffmpeg/libavformat/jvdec.c (revision cabdff1a)
1/*
2 * Bitmap Brothers JV demuxer
3 * Copyright (c) 2005, 2011 Peter Ross <pross@xvid.org>
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 * Bitmap Brothers JV demuxer
25 * @author Peter Ross <pross@xvid.org>
26 */
27
28#include "libavutil/channel_layout.h"
29#include "libavutil/intreadwrite.h"
30
31#include "avformat.h"
32#include "internal.h"
33
34#define JV_PREAMBLE_SIZE 5
35
36typedef struct JVFrame {
37    int audio_size;    /**< audio packet size (bytes) */
38    int video_size;    /**< video packet size (bytes) */
39    uint16_t palette_size;  /**< palette size (bytes) */
40    uint8_t video_type;     /**< per-frame video compression type */
41} JVFrame;
42
43typedef struct JVDemuxContext {
44    JVFrame *frames;
45    enum {
46        JV_AUDIO = 0,
47        JV_VIDEO,
48        JV_PADDING
49    } state;
50    int64_t pts;
51} JVDemuxContext;
52
53#define MAGIC " Compression by John M Phillips Copyright (C) 1995 The Bitmap Brothers Ltd."
54
55static int read_probe(const AVProbeData *pd)
56{
57    if (pd->buf[0] == 'J' && pd->buf[1] == 'V' && strlen(MAGIC) + 4 <= pd->buf_size &&
58        !memcmp(pd->buf + 4, MAGIC, strlen(MAGIC)))
59        return AVPROBE_SCORE_MAX;
60    return 0;
61}
62
63static int read_close(AVFormatContext *s)
64{
65    JVDemuxContext *jv = s->priv_data;
66
67    av_freep(&jv->frames);
68
69    return 0;
70}
71
72static int read_header(AVFormatContext *s)
73{
74    JVDemuxContext *jv = s->priv_data;
75    AVIOContext *pb = s->pb;
76    AVStream *vst, *ast;
77    FFStream *asti;
78    int64_t audio_pts = 0;
79    int64_t offset;
80
81    avio_skip(pb, 80);
82
83    ast = avformat_new_stream(s, NULL);
84    vst = avformat_new_stream(s, NULL);
85    if (!ast || !vst)
86        return AVERROR(ENOMEM);
87    asti = ffstream(ast);
88
89    vst->codecpar->codec_type  = AVMEDIA_TYPE_VIDEO;
90    vst->codecpar->codec_id    = AV_CODEC_ID_JV;
91    vst->codecpar->codec_tag   = 0; /* no fourcc */
92    vst->codecpar->width       = avio_rl16(pb);
93    vst->codecpar->height      = avio_rl16(pb);
94    vst->duration           =
95    vst->nb_frames          =
96    asti->nb_index_entries  = avio_rl16(pb);
97    avpriv_set_pts_info(vst, 64, avio_rl16(pb), 1000);
98
99    avio_skip(pb, 4);
100
101    ast->codecpar->codec_type     = AVMEDIA_TYPE_AUDIO;
102    ast->codecpar->codec_id       = AV_CODEC_ID_PCM_U8;
103    ast->codecpar->codec_tag      = 0; /* no fourcc */
104    ast->codecpar->sample_rate    = avio_rl16(pb);
105    ast->codecpar->ch_layout      = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
106    avpriv_set_pts_info(ast, 64, 1, ast->codecpar->sample_rate);
107
108    avio_skip(pb, 10);
109
110    asti->index_entries = av_malloc(asti->nb_index_entries *
111                                    sizeof(*asti->index_entries));
112    if (!asti->index_entries)
113        return AVERROR(ENOMEM);
114
115    jv->frames = av_malloc(asti->nb_index_entries * sizeof(*jv->frames));
116    if (!jv->frames)
117        return AVERROR(ENOMEM);
118    offset = 0x68 + asti->nb_index_entries * 16;
119    for (int i = 0; i < asti->nb_index_entries; i++) {
120        AVIndexEntry *e   = asti->index_entries + i;
121        JVFrame      *jvf = jv->frames + i;
122
123        /* total frame size including audio, video, palette data and padding */
124        e->size      = avio_rl32(pb);
125        e->timestamp = i;
126        e->pos       = offset;
127        offset      += e->size;
128
129        jvf->audio_size   = avio_rl32(pb);
130        jvf->video_size   = avio_rl32(pb);
131        jvf->palette_size = avio_r8(pb) ? 768 : 0;
132
133        if ((jvf->video_size | jvf->audio_size) & ~0xFFFFFF ||
134            e->size - jvf->audio_size
135                    - jvf->video_size
136                    - jvf->palette_size < 0) {
137            if (s->error_recognition & AV_EF_EXPLODE)
138                return AVERROR_INVALIDDATA;
139            jvf->audio_size   =
140            jvf->video_size   =
141            jvf->palette_size = 0;
142        }
143
144        if (avio_r8(pb))
145            av_log(s, AV_LOG_WARNING, "unsupported audio codec\n");
146
147        jvf->video_type = avio_r8(pb);
148        avio_skip(pb, 1);
149
150        e->timestamp = jvf->audio_size ? audio_pts : AV_NOPTS_VALUE;
151        audio_pts   += jvf->audio_size;
152
153        e->flags = jvf->video_type != 1 ? AVINDEX_KEYFRAME : 0;
154    }
155
156    jv->state = JV_AUDIO;
157    return 0;
158}
159
160static int read_packet(AVFormatContext *s, AVPacket *pkt)
161{
162    JVDemuxContext *jv = s->priv_data;
163    AVIOContext *pb = s->pb;
164    AVStream *ast = s->streams[0];
165    FFStream *const asti = ffstream(ast);
166    int ret;
167
168    while (!avio_feof(s->pb) && jv->pts < asti->nb_index_entries) {
169        const AVIndexEntry *const e = asti->index_entries + jv->pts;
170        const JVFrame      *jvf = jv->frames + jv->pts;
171
172        switch (jv->state) {
173        case JV_AUDIO:
174            jv->state++;
175            if (jvf->audio_size) {
176                if ((ret = av_get_packet(s->pb, pkt, jvf->audio_size)) < 0)
177                    return ret;
178                pkt->stream_index = 0;
179                pkt->pts          = e->timestamp;
180                pkt->flags       |= AV_PKT_FLAG_KEY;
181                return 0;
182            }
183        case JV_VIDEO:
184            jv->state++;
185            if (jvf->video_size || jvf->palette_size) {
186                int size = jvf->video_size + jvf->palette_size;
187                if ((ret = av_new_packet(pkt, size + JV_PREAMBLE_SIZE)) < 0)
188                    return ret;
189
190                AV_WL32(pkt->data, jvf->video_size);
191                pkt->data[4] = jvf->video_type;
192                ret = avio_read(pb, pkt->data + JV_PREAMBLE_SIZE, size);
193                if (ret < 0)
194                    return ret;
195                if (ret < size) {
196                    memset(pkt->data + JV_PREAMBLE_SIZE + ret, 0,
197                           AV_INPUT_BUFFER_PADDING_SIZE);
198                    pkt->flags |= AV_PKT_FLAG_CORRUPT;
199                }
200                pkt->size         = ret + JV_PREAMBLE_SIZE;
201                pkt->stream_index = 1;
202                pkt->pts          = jv->pts;
203                if (jvf->video_type != 1)
204                    pkt->flags |= AV_PKT_FLAG_KEY;
205                return 0;
206            }
207        case JV_PADDING:
208            avio_skip(pb, FFMAX(e->size - jvf->audio_size - jvf->video_size
209                                        - jvf->palette_size, 0));
210            jv->state = JV_AUDIO;
211            jv->pts++;
212        }
213    }
214
215    if (s->pb->eof_reached)
216        return AVERROR_EOF;
217
218    return AVERROR(EIO);
219}
220
221static int read_seek(AVFormatContext *s, int stream_index,
222                     int64_t ts, int flags)
223{
224    JVDemuxContext *jv = s->priv_data;
225    AVStream *ast = s->streams[0];
226    FFStream *const asti = ffstream(ast);
227    int i;
228
229    if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
230        return AVERROR(ENOSYS);
231
232    switch (stream_index) {
233    case 0:
234        i = av_index_search_timestamp(ast, ts, flags);
235        break;
236    case 1:
237        i = ts;
238        break;
239    default:
240        return 0;
241    }
242
243    if (i < 0 || i >= asti->nb_index_entries)
244        return 0;
245    if (avio_seek(s->pb, asti->index_entries[i].pos, SEEK_SET) < 0)
246        return -1;
247
248    jv->state = JV_AUDIO;
249    jv->pts   = i;
250    return 0;
251}
252
253const AVInputFormat ff_jv_demuxer = {
254    .name           = "jv",
255    .long_name      = NULL_IF_CONFIG_SMALL("Bitmap Brothers JV"),
256    .priv_data_size = sizeof(JVDemuxContext),
257    .flags_internal = FF_FMT_INIT_CLEANUP,
258    .read_probe     = read_probe,
259    .read_header    = read_header,
260    .read_packet    = read_packet,
261    .read_seek      = read_seek,
262    .read_close     = read_close,
263};
264