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"
40 #define TMV_TAG MKTAG('T', 'M', 'A', 'V')
42 typedef struct TMVContext
{
43 unsigned audio_chunk_size
;
44 unsigned video_chunk_size
;
46 unsigned stream_index
;
49 #define TMV_HEADER_SIZE 12
51 #define PROBE_MIN_SAMPLE_RATE 5000
52 #define PROBE_MAX_FPS 120
53 #define PROBE_MIN_AUDIO_SIZE (PROBE_MIN_SAMPLE_RATE / PROBE_MAX_FPS)
55 static int tmv_probe(const AVProbeData
*p
)
57 if (AV_RL32(p
->buf
) == TMV_TAG
&&
58 AV_RL16(p
->buf
+4) >= PROBE_MIN_SAMPLE_RATE
&&
59 AV_RL16(p
->buf
+6) >= PROBE_MIN_AUDIO_SIZE
&&
60 !p
->buf
[8] && // compression method
61 p
->buf
[9] && // char cols
62 p
->buf
[10]) // char rows
63 return AVPROBE_SCORE_MAX
/
64 ((p
->buf
[9] == 40 && p
->buf
[10] == 25) ? 1 : 4);
68 static int tmv_read_header(AVFormatContext
*s
)
70 TMVContext
*tmv
= s
->priv_data
;
71 AVIOContext
*pb
= s
->pb
;
74 unsigned comp_method
, char_cols
, char_rows
, features
;
76 if (avio_rl32(pb
) != TMV_TAG
)
79 if (!(vst
= avformat_new_stream(s
, NULL
)))
80 return AVERROR(ENOMEM
);
82 if (!(ast
= avformat_new_stream(s
, NULL
)))
83 return AVERROR(ENOMEM
);
85 ast
->codecpar
->sample_rate
= avio_rl16(pb
);
86 if (!ast
->codecpar
->sample_rate
) {
87 av_log(s
, AV_LOG_ERROR
, "invalid sample rate\n");
91 tmv
->audio_chunk_size
= avio_rl16(pb
);
92 if (!tmv
->audio_chunk_size
) {
93 av_log(s
, AV_LOG_ERROR
, "invalid audio chunk size\n");
97 comp_method
= avio_r8(pb
);
99 av_log(s
, AV_LOG_ERROR
, "unsupported compression method %d\n",
104 char_cols
= avio_r8(pb
);
105 char_rows
= avio_r8(pb
);
106 tmv
->video_chunk_size
= char_cols
* char_rows
* 2;
107 if (!tmv
->video_chunk_size
) {
108 av_log(s
, AV_LOG_ERROR
, "invalid video chunk size\n");
109 return AVERROR_INVALIDDATA
;
112 features
= avio_r8(pb
);
113 if (features
& ~(TMV_PADDING
| TMV_STEREO
)) {
114 av_log(s
, AV_LOG_ERROR
, "unsupported features 0x%02x\n",
115 features
& ~(TMV_PADDING
| TMV_STEREO
));
119 ast
->codecpar
->codec_type
= AVMEDIA_TYPE_AUDIO
;
120 ast
->codecpar
->codec_id
= AV_CODEC_ID_PCM_U8
;
121 av_channel_layout_default(&ast
->codecpar
->ch_layout
, !!(features
& TMV_STEREO
) + 1);
122 ast
->codecpar
->bits_per_coded_sample
= 8;
123 ast
->codecpar
->bit_rate
= ast
->codecpar
->sample_rate
*
124 ast
->codecpar
->bits_per_coded_sample
;
125 avpriv_set_pts_info(ast
, 32, 1, ast
->codecpar
->sample_rate
);
127 fps
.num
= ast
->codecpar
->sample_rate
* ast
->codecpar
->ch_layout
.nb_channels
;
128 fps
.den
= tmv
->audio_chunk_size
;
129 av_reduce(&fps
.num
, &fps
.den
, fps
.num
, fps
.den
, 0xFFFFFFFFLL
);
131 vst
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
132 vst
->codecpar
->codec_id
= AV_CODEC_ID_TMV
;
133 vst
->codecpar
->format
= AV_PIX_FMT_PAL8
;
134 vst
->codecpar
->width
= char_cols
* 8;
135 vst
->codecpar
->height
= char_rows
* 8;
136 avpriv_set_pts_info(vst
, 32, fps
.den
, fps
.num
);
138 if (features
& TMV_PADDING
)
140 ((tmv
->video_chunk_size
+ tmv
->audio_chunk_size
+ 511) & ~511) -
141 (tmv
->video_chunk_size
+ tmv
->audio_chunk_size
);
143 vst
->codecpar
->bit_rate
= ((tmv
->video_chunk_size
+ tmv
->padding
) *
144 fps
.num
* 8) / fps
.den
;
149 static int tmv_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
151 TMVContext
*tmv
= s
->priv_data
;
152 AVIOContext
*pb
= s
->pb
;
153 int ret
, pkt_size
= tmv
->stream_index
?
154 tmv
->audio_chunk_size
: tmv
->video_chunk_size
;
159 ret
= av_get_packet(pb
, pkt
, pkt_size
);
161 if (tmv
->stream_index
)
162 avio_skip(pb
, tmv
->padding
);
164 pkt
->stream_index
= tmv
->stream_index
;
165 tmv
->stream_index
^= 1;
166 pkt
->flags
|= AV_PKT_FLAG_KEY
;
171 static int tmv_read_seek(AVFormatContext
*s
, int stream_index
,
172 int64_t timestamp
, int flags
)
174 TMVContext
*tmv
= s
->priv_data
;
181 (tmv
->audio_chunk_size
+ tmv
->video_chunk_size
+ tmv
->padding
);
183 if (avio_seek(s
->pb
, pos
+ TMV_HEADER_SIZE
, SEEK_SET
) < 0)
185 tmv
->stream_index
= 0;
189 const FFInputFormat ff_tmv_demuxer
= {
191 .p
.long_name
= NULL_IF_CONFIG_SMALL("8088flex TMV"),
192 .p
.flags
= AVFMT_GENERIC_INDEX
,
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
,