ffmpeg / libavformat / tmv.c @ 72415b2a
History | View | Annotate | Download (5.48 KB)
1 | 7c7d3e09 | Daniel Verkamp | /*
|
---|---|---|---|
2 | * 8088flex TMV file demuxer
|
||
3 | * Copyright (c) 2009 Daniel Verkamp <daniel at drv.nu>
|
||
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 | * 8088flex TMV file demuxer
|
||
24 | * @file libavformat/tmv.c
|
||
25 | * @author Daniel Verkamp
|
||
26 | * @sa http://www.oldskool.org/pc/8088_Corruption
|
||
27 | */
|
||
28 | |||
29 | #include "libavutil/intreadwrite.h" |
||
30 | #include "avformat.h" |
||
31 | |||
32 | enum {
|
||
33 | TMV_PADDING = 0x01,
|
||
34 | TMV_STEREO = 0x02,
|
||
35 | }; |
||
36 | |||
37 | #define TMV_TAG MKTAG('T', 'M', 'A', 'V') |
||
38 | |||
39 | typedef struct TMVContext { |
||
40 | unsigned audio_chunk_size;
|
||
41 | unsigned video_chunk_size;
|
||
42 | unsigned padding;
|
||
43 | unsigned stream_index;
|
||
44 | } TMVContext; |
||
45 | |||
46 | 61ee5045 | Daniel Verkamp | #define TMV_HEADER_SIZE 12 |
47 | |||
48 | f19ae9ea | Daniel Verkamp | #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)
|
||
51 | |||
52 | 7c7d3e09 | Daniel Verkamp | static int tmv_probe(AVProbeData *p) |
53 | { |
||
54 | f19ae9ea | Daniel Verkamp | 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 | 0319ba5e | Daniel Verkamp | return AVPROBE_SCORE_MAX /
|
61 | ((p->buf[9] == 40 && p->buf[10] == 25) ? 1 : 4); |
||
62 | 7c7d3e09 | Daniel Verkamp | return 0; |
63 | } |
||
64 | |||
65 | static int tmv_read_header(AVFormatContext *s, AVFormatParameters *ap) |
||
66 | { |
||
67 | TMVContext *tmv = s->priv_data; |
||
68 | ByteIOContext *pb = s->pb; |
||
69 | AVStream *vst, *ast; |
||
70 | AVRational fps; |
||
71 | unsigned comp_method, char_cols, char_rows, features;
|
||
72 | |||
73 | if (get_le32(pb) != TMV_TAG)
|
||
74 | return -1; |
||
75 | |||
76 | if (!(vst = av_new_stream(s, 0))) |
||
77 | return AVERROR(ENOMEM);
|
||
78 | |||
79 | if (!(ast = av_new_stream(s, 0))) |
||
80 | return AVERROR(ENOMEM);
|
||
81 | |||
82 | ast->codec->sample_rate = get_le16(pb); |
||
83 | 54eb4ae0 | Daniel Verkamp | if (!ast->codec->sample_rate) {
|
84 | av_log(s, AV_LOG_ERROR, "invalid sample rate\n");
|
||
85 | return -1; |
||
86 | } |
||
87 | |||
88 | 7c7d3e09 | Daniel Verkamp | 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");
|
||
91 | return -1; |
||
92 | } |
||
93 | |||
94 | comp_method = get_byte(pb); |
||
95 | if (comp_method) {
|
||
96 | av_log(s, AV_LOG_ERROR, "unsupported compression method %d\n",
|
||
97 | comp_method); |
||
98 | return -1; |
||
99 | } |
||
100 | |||
101 | char_cols = get_byte(pb); |
||
102 | char_rows = get_byte(pb); |
||
103 | tmv->video_chunk_size = char_cols * char_rows * 2;
|
||
104 | |||
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)); |
||
109 | return -1; |
||
110 | } |
||
111 | |||
112 | 72415b2a | Stefano Sabatini | ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
113 | 7c7d3e09 | Daniel Verkamp | 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); |
||
119 | |||
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);
|
||
123 | |||
124 | 72415b2a | Stefano Sabatini | vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
125 | 7c7d3e09 | Daniel Verkamp | 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);
|
||
130 | |||
131 | if (features & TMV_PADDING)
|
||
132 | tmv->padding = |
||
133 | ((tmv->video_chunk_size + tmv->audio_chunk_size + 511) & ~511) - |
||
134 | (tmv->video_chunk_size + tmv->audio_chunk_size); |
||
135 | |||
136 | vst->codec->bit_rate = ((tmv->video_chunk_size + tmv->padding) * |
||
137 | fps.num * 8) / fps.den;
|
||
138 | |||
139 | return 0; |
||
140 | } |
||
141 | |||
142 | static int tmv_read_packet(AVFormatContext *s, AVPacket *pkt) |
||
143 | { |
||
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; |
||
148 | |||
149 | if (url_feof(pb))
|
||
150 | return AVERROR_EOF;
|
||
151 | |||
152 | ret = av_get_packet(pb, pkt, pkt_size); |
||
153 | |||
154 | if (tmv->stream_index)
|
||
155 | url_fskip(pb, tmv->padding); |
||
156 | |||
157 | pkt->stream_index = tmv->stream_index; |
||
158 | tmv->stream_index ^= 1;
|
||
159 | pkt->flags |= PKT_FLAG_KEY; |
||
160 | |||
161 | return ret;
|
||
162 | } |
||
163 | |||
164 | 61ee5045 | Daniel Verkamp | static int tmv_read_seek(AVFormatContext *s, int stream_index, |
165 | int64_t timestamp, int flags)
|
||
166 | { |
||
167 | TMVContext *tmv = s->priv_data; |
||
168 | int64_t pos; |
||
169 | |||
170 | if (stream_index)
|
||
171 | return -1; |
||
172 | |||
173 | pos = timestamp * |
||
174 | (tmv->audio_chunk_size + tmv->video_chunk_size + tmv->padding); |
||
175 | |||
176 | url_fseek(s->pb, pos + TMV_HEADER_SIZE, SEEK_SET); |
||
177 | tmv->stream_index = 0;
|
||
178 | return 0; |
||
179 | } |
||
180 | |||
181 | 7c7d3e09 | Daniel Verkamp | AVInputFormat tmv_demuxer = { |
182 | "tmv",
|
||
183 | NULL_IF_CONFIG_SMALL("8088flex TMV"),
|
||
184 | sizeof(TMVContext),
|
||
185 | tmv_probe, |
||
186 | tmv_read_header, |
||
187 | tmv_read_packet, |
||
188 | 61ee5045 | Daniel Verkamp | NULL,
|
189 | tmv_read_seek, |
||
190 | 7c7d3e09 | Daniel Verkamp | .flags = AVFMT_GENERIC_INDEX, |
191 | }; |