2 * 8088flex TMV file demuxer
3 * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
5 * This file is part of FFmpeg.
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.
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.
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
24 * 8088flex TMV file demuxer
25 * @author Daniel Verkamp
26 * @see http://www.oldskool.org/pc/8088_Corruption
29 #include "libavutil/channel_layout.h"
30 #include "libavutil/intreadwrite.h"
39 #define TMV_TAG MKTAG('T', 'M', 'A', 'V')
41 typedef struct TMVContext
{
42 unsigned audio_chunk_size
;
43 unsigned video_chunk_size
;
45 unsigned stream_index
;
48 #define TMV_HEADER_SIZE 12
50 #define PROBE_MIN_SAMPLE_RATE 5000
51 #define PROBE_MAX_FPS 120
52 #define PROBE_MIN_AUDIO_SIZE (PROBE_MIN_SAMPLE_RATE / PROBE_MAX_FPS)
54 static int tmv_probe(AVProbeData
*p
)
56 if (AV_RL32(p
->buf
) == TMV_TAG
&&
57 AV_RL16(p
->buf
+4) >= PROBE_MIN_SAMPLE_RATE
&&
58 AV_RL16(p
->buf
+6) >= PROBE_MIN_AUDIO_SIZE
&&
59 !p
->buf
[8] && // compression method
60 p
->buf
[9] && // char cols
61 p
->buf
[10]) // char rows
62 return AVPROBE_SCORE_MAX
/
63 ((p
->buf
[9] == 40 && p
->buf
[10] == 25) ? 1 : 4);
67 static int tmv_read_header(AVFormatContext
*s
)
69 TMVContext
*tmv
= s
->priv_data
;
70 AVIOContext
*pb
= s
->pb
;
73 unsigned comp_method
, char_cols
, char_rows
, features
;
75 if (avio_rl32(pb
) != TMV_TAG
)
78 if (!(vst
= avformat_new_stream(s
, NULL
)))
79 return AVERROR(ENOMEM
);
81 if (!(ast
= avformat_new_stream(s
, NULL
)))
82 return AVERROR(ENOMEM
);
84 ast
->codec
->sample_rate
= avio_rl16(pb
);
85 if (!ast
->codec
->sample_rate
) {
86 av_log(s
, AV_LOG_ERROR
, "invalid sample rate\n");
90 tmv
->audio_chunk_size
= avio_rl16(pb
);
91 if (!tmv
->audio_chunk_size
) {
92 av_log(s
, AV_LOG_ERROR
, "invalid audio chunk size\n");
96 comp_method
= avio_r8(pb
);
98 av_log(s
, AV_LOG_ERROR
, "unsupported compression method %d\n",
103 char_cols
= avio_r8(pb
);
104 char_rows
= avio_r8(pb
);
105 tmv
->video_chunk_size
= char_cols
* char_rows
* 2;
107 features
= avio_r8(pb
);
108 if (features
& ~(TMV_PADDING
| TMV_STEREO
)) {
109 av_log(s
, AV_LOG_ERROR
, "unsupported features 0x%02x\n",
110 features
& ~(TMV_PADDING
| TMV_STEREO
));
114 ast
->codec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
115 ast
->codec
->codec_id
= AV_CODEC_ID_PCM_U8
;
116 if (features
& TMV_STEREO
) {
117 ast
->codec
->channels
= 2;
118 ast
->codec
->channel_layout
= AV_CH_LAYOUT_STEREO
;
120 ast
->codec
->channels
= 1;
121 ast
->codec
->channel_layout
= AV_CH_LAYOUT_MONO
;
123 ast
->codec
->bits_per_coded_sample
= 8;
124 ast
->codec
->bit_rate
= ast
->codec
->sample_rate
*
125 ast
->codec
->bits_per_coded_sample
;
126 avpriv_set_pts_info(ast
, 32, 1, ast
->codec
->sample_rate
);
128 fps
.num
= ast
->codec
->sample_rate
* ast
->codec
->channels
;
129 fps
.den
= tmv
->audio_chunk_size
;
130 av_reduce(&fps
.num
, &fps
.den
, fps
.num
, fps
.den
, 0xFFFFFFFFLL
);
132 vst
->codec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
133 vst
->codec
->codec_id
= AV_CODEC_ID_TMV
;
134 vst
->codec
->pix_fmt
= AV_PIX_FMT_PAL8
;
135 vst
->codec
->width
= char_cols
* 8;
136 vst
->codec
->height
= char_rows
* 8;
137 avpriv_set_pts_info(vst
, 32, fps
.den
, fps
.num
);
139 if (features
& TMV_PADDING
)
141 ((tmv
->video_chunk_size
+ tmv
->audio_chunk_size
+ 511) & ~511) -
142 (tmv
->video_chunk_size
+ tmv
->audio_chunk_size
);
144 vst
->codec
->bit_rate
= ((tmv
->video_chunk_size
+ tmv
->padding
) *
145 fps
.num
* 8) / fps
.den
;
150 static int tmv_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
152 TMVContext
*tmv
= s
->priv_data
;
153 AVIOContext
*pb
= s
->pb
;
154 int ret
, pkt_size
= tmv
->stream_index
?
155 tmv
->audio_chunk_size
: tmv
->video_chunk_size
;
160 ret
= av_get_packet(pb
, pkt
, pkt_size
);
162 if (tmv
->stream_index
)
163 avio_skip(pb
, tmv
->padding
);
165 pkt
->stream_index
= tmv
->stream_index
;
166 tmv
->stream_index
^= 1;
167 pkt
->flags
|= AV_PKT_FLAG_KEY
;
172 static int tmv_read_seek(AVFormatContext
*s
, int stream_index
,
173 int64_t timestamp
, int flags
)
175 TMVContext
*tmv
= s
->priv_data
;
182 (tmv
->audio_chunk_size
+ tmv
->video_chunk_size
+ tmv
->padding
);
184 if (avio_seek(s
->pb
, pos
+ TMV_HEADER_SIZE
, SEEK_SET
) < 0)
186 tmv
->stream_index
= 0;
190 AVInputFormat ff_tmv_demuxer
= {
192 .long_name
= NULL_IF_CONFIG_SMALL("8088flex TMV"),
193 .priv_data_size
= sizeof(TMVContext
),
194 .read_probe
= tmv_probe
,
195 .read_header
= tmv_read_header
,
196 .read_packet
= tmv_read_packet
,
197 .read_seek
= tmv_read_seek
,
198 .flags
= AVFMT_GENERIC_INDEX
,