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
23 * 8088flex TMV file demuxer
24 * @file libavformat/tmv.c
25 * @author Daniel Verkamp
26 * @sa http://www.oldskool.org/pc/8088_Corruption
29 #include "libavutil/intreadwrite.h"
37 #define TMV_TAG MKTAG('T', 'M', 'A', 'V')
39 typedef struct TMVContext
{
40 unsigned audio_chunk_size
;
41 unsigned video_chunk_size
;
43 unsigned stream_index
;
46 #define TMV_HEADER_SIZE 12
48 #define PROBE_MIN_SAMPLE_RATE 5000
49 #define PROBE_MAX_FPS 120
50 #define PROBE_MIN_AUDIO_SIZE (PROBE_MIN_SAMPLE_RATE / PROBE_MAX_FPS)
52 static int tmv_probe(AVProbeData
*p
)
54 if (AV_RL32(p
->buf
) == TMV_TAG
&&
55 AV_RL16(p
->buf
+4) >= PROBE_MIN_SAMPLE_RATE
&&
56 AV_RL16(p
->buf
+6) >= PROBE_MIN_AUDIO_SIZE
&&
57 !p
->buf
[8] && // compression method
58 p
->buf
[9] && // char cols
59 p
->buf
[10]) // char rows
60 return AVPROBE_SCORE_MAX
/
61 ((p
->buf
[9] == 40 && p
->buf
[10] == 25) ? 1 : 4);
65 static int tmv_read_header(AVFormatContext
*s
, AVFormatParameters
*ap
)
67 TMVContext
*tmv
= s
->priv_data
;
68 ByteIOContext
*pb
= s
->pb
;
71 unsigned comp_method
, char_cols
, char_rows
, features
;
73 if (get_le32(pb
) != TMV_TAG
)
76 if (!(vst
= av_new_stream(s
, 0)))
77 return AVERROR(ENOMEM
);
79 if (!(ast
= av_new_stream(s
, 0)))
80 return AVERROR(ENOMEM
);
82 ast
->codec
->sample_rate
= get_le16(pb
);
83 if (!ast
->codec
->sample_rate
) {
84 av_log(s
, AV_LOG_ERROR
, "invalid sample rate\n");
88 tmv
->audio_chunk_size
= get_le16(pb
);
89 if (!tmv
->audio_chunk_size
) {
90 av_log(s
, AV_LOG_ERROR
, "invalid audio chunk size\n");
94 comp_method
= get_byte(pb
);
96 av_log(s
, AV_LOG_ERROR
, "unsupported compression method %d\n",
101 char_cols
= get_byte(pb
);
102 char_rows
= get_byte(pb
);
103 tmv
->video_chunk_size
= char_cols
* char_rows
* 2;
105 features
= get_byte(pb
);
106 if (features
& ~(TMV_PADDING
| TMV_STEREO
)) {
107 av_log(s
, AV_LOG_ERROR
, "unsupported features 0x%02x\n",
108 features
& ~(TMV_PADDING
| TMV_STEREO
));
112 ast
->codec
->codec_type
= CODEC_TYPE_AUDIO
;
113 ast
->codec
->codec_id
= CODEC_ID_PCM_U8
;
114 ast
->codec
->channels
= features
& TMV_STEREO
? 2 : 1;
115 ast
->codec
->bits_per_coded_sample
= 8;
116 ast
->codec
->bit_rate
= ast
->codec
->sample_rate
*
117 ast
->codec
->bits_per_coded_sample
;
118 av_set_pts_info(ast
, 32, 1, ast
->codec
->sample_rate
);
120 fps
.num
= ast
->codec
->sample_rate
* ast
->codec
->channels
;
121 fps
.den
= tmv
->audio_chunk_size
;
122 av_reduce(&fps
.num
, &fps
.den
, fps
.num
, fps
.den
, 0xFFFFFFFFLL
);
124 vst
->codec
->codec_type
= CODEC_TYPE_VIDEO
;
125 vst
->codec
->codec_id
= CODEC_ID_TMV
;
126 vst
->codec
->pix_fmt
= PIX_FMT_PAL8
;
127 vst
->codec
->width
= char_cols
* 8;
128 vst
->codec
->height
= char_rows
* 8;
129 av_set_pts_info(vst
, 32, fps
.den
, fps
.num
);
131 if (features
& TMV_PADDING
)
133 ((tmv
->video_chunk_size
+ tmv
->audio_chunk_size
+ 511) & ~511) -
134 (tmv
->video_chunk_size
+ tmv
->audio_chunk_size
);
136 vst
->codec
->bit_rate
= ((tmv
->video_chunk_size
+ tmv
->padding
) *
137 fps
.num
* 8) / fps
.den
;
142 static int tmv_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
144 TMVContext
*tmv
= s
->priv_data
;
145 ByteIOContext
*pb
= s
->pb
;
146 int ret
, pkt_size
= tmv
->stream_index
?
147 tmv
->audio_chunk_size
: tmv
->video_chunk_size
;
152 ret
= av_get_packet(pb
, pkt
, pkt_size
);
154 if (tmv
->stream_index
)
155 url_fskip(pb
, tmv
->padding
);
157 pkt
->stream_index
= tmv
->stream_index
;
158 tmv
->stream_index
^= 1;
159 pkt
->flags
|= PKT_FLAG_KEY
;
164 static int tmv_read_seek(AVFormatContext
*s
, int stream_index
,
165 int64_t timestamp
, int flags
)
167 TMVContext
*tmv
= s
->priv_data
;
174 (tmv
->audio_chunk_size
+ tmv
->video_chunk_size
+ tmv
->padding
);
176 url_fseek(s
->pb
, pos
+ TMV_HEADER_SIZE
, SEEK_SET
);
177 tmv
->stream_index
= 0;
181 AVInputFormat tmv_demuxer
= {
183 NULL_IF_CONFIG_SMALL("8088flex TMV"),
190 .flags
= AVFMT_GENERIC_INDEX
,