yading@11: /* yading@11: * 8088flex TMV file demuxer yading@11: * Copyright (c) 2009 Daniel Verkamp yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * 8088flex TMV file demuxer yading@11: * @author Daniel Verkamp yading@11: * @see http://www.oldskool.org/pc/8088_Corruption yading@11: */ yading@11: yading@11: #include "libavutil/channel_layout.h" yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: yading@11: enum { yading@11: TMV_PADDING = 0x01, yading@11: TMV_STEREO = 0x02, yading@11: }; yading@11: yading@11: #define TMV_TAG MKTAG('T', 'M', 'A', 'V') yading@11: yading@11: typedef struct TMVContext { yading@11: unsigned audio_chunk_size; yading@11: unsigned video_chunk_size; yading@11: unsigned padding; yading@11: unsigned stream_index; yading@11: } TMVContext; yading@11: yading@11: #define TMV_HEADER_SIZE 12 yading@11: yading@11: #define PROBE_MIN_SAMPLE_RATE 5000 yading@11: #define PROBE_MAX_FPS 120 yading@11: #define PROBE_MIN_AUDIO_SIZE (PROBE_MIN_SAMPLE_RATE / PROBE_MAX_FPS) yading@11: yading@11: static int tmv_probe(AVProbeData *p) yading@11: { yading@11: if (AV_RL32(p->buf) == TMV_TAG && yading@11: AV_RL16(p->buf+4) >= PROBE_MIN_SAMPLE_RATE && yading@11: AV_RL16(p->buf+6) >= PROBE_MIN_AUDIO_SIZE && yading@11: !p->buf[8] && // compression method yading@11: p->buf[9] && // char cols yading@11: p->buf[10]) // char rows yading@11: return AVPROBE_SCORE_MAX / yading@11: ((p->buf[9] == 40 && p->buf[10] == 25) ? 1 : 4); yading@11: return 0; yading@11: } yading@11: yading@11: static int tmv_read_header(AVFormatContext *s) yading@11: { yading@11: TMVContext *tmv = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVStream *vst, *ast; yading@11: AVRational fps; yading@11: unsigned comp_method, char_cols, char_rows, features; yading@11: yading@11: if (avio_rl32(pb) != TMV_TAG) yading@11: return -1; yading@11: yading@11: if (!(vst = avformat_new_stream(s, NULL))) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: if (!(ast = avformat_new_stream(s, NULL))) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: ast->codec->sample_rate = avio_rl16(pb); yading@11: if (!ast->codec->sample_rate) { yading@11: av_log(s, AV_LOG_ERROR, "invalid sample rate\n"); yading@11: return -1; yading@11: } yading@11: yading@11: tmv->audio_chunk_size = avio_rl16(pb); yading@11: if (!tmv->audio_chunk_size) { yading@11: av_log(s, AV_LOG_ERROR, "invalid audio chunk size\n"); yading@11: return -1; yading@11: } yading@11: yading@11: comp_method = avio_r8(pb); yading@11: if (comp_method) { yading@11: av_log(s, AV_LOG_ERROR, "unsupported compression method %d\n", yading@11: comp_method); yading@11: return -1; yading@11: } yading@11: yading@11: char_cols = avio_r8(pb); yading@11: char_rows = avio_r8(pb); yading@11: tmv->video_chunk_size = char_cols * char_rows * 2; yading@11: yading@11: features = avio_r8(pb); yading@11: if (features & ~(TMV_PADDING | TMV_STEREO)) { yading@11: av_log(s, AV_LOG_ERROR, "unsupported features 0x%02x\n", yading@11: features & ~(TMV_PADDING | TMV_STEREO)); yading@11: return -1; yading@11: } yading@11: yading@11: ast->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: ast->codec->codec_id = AV_CODEC_ID_PCM_U8; yading@11: if (features & TMV_STEREO) { yading@11: ast->codec->channels = 2; yading@11: ast->codec->channel_layout = AV_CH_LAYOUT_STEREO; yading@11: } else { yading@11: ast->codec->channels = 1; yading@11: ast->codec->channel_layout = AV_CH_LAYOUT_MONO; yading@11: } yading@11: ast->codec->bits_per_coded_sample = 8; yading@11: ast->codec->bit_rate = ast->codec->sample_rate * yading@11: ast->codec->bits_per_coded_sample; yading@11: avpriv_set_pts_info(ast, 32, 1, ast->codec->sample_rate); yading@11: yading@11: fps.num = ast->codec->sample_rate * ast->codec->channels; yading@11: fps.den = tmv->audio_chunk_size; yading@11: av_reduce(&fps.num, &fps.den, fps.num, fps.den, 0xFFFFFFFFLL); yading@11: yading@11: vst->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: vst->codec->codec_id = AV_CODEC_ID_TMV; yading@11: vst->codec->pix_fmt = AV_PIX_FMT_PAL8; yading@11: vst->codec->width = char_cols * 8; yading@11: vst->codec->height = char_rows * 8; yading@11: avpriv_set_pts_info(vst, 32, fps.den, fps.num); yading@11: yading@11: if (features & TMV_PADDING) yading@11: tmv->padding = yading@11: ((tmv->video_chunk_size + tmv->audio_chunk_size + 511) & ~511) - yading@11: (tmv->video_chunk_size + tmv->audio_chunk_size); yading@11: yading@11: vst->codec->bit_rate = ((tmv->video_chunk_size + tmv->padding) * yading@11: fps.num * 8) / fps.den; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int tmv_read_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: TMVContext *tmv = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: int ret, pkt_size = tmv->stream_index ? yading@11: tmv->audio_chunk_size : tmv->video_chunk_size; yading@11: yading@11: if (url_feof(pb)) yading@11: return AVERROR_EOF; yading@11: yading@11: ret = av_get_packet(pb, pkt, pkt_size); yading@11: yading@11: if (tmv->stream_index) yading@11: avio_skip(pb, tmv->padding); yading@11: yading@11: pkt->stream_index = tmv->stream_index; yading@11: tmv->stream_index ^= 1; yading@11: pkt->flags |= AV_PKT_FLAG_KEY; yading@11: yading@11: return ret; yading@11: } yading@11: yading@11: static int tmv_read_seek(AVFormatContext *s, int stream_index, yading@11: int64_t timestamp, int flags) yading@11: { yading@11: TMVContext *tmv = s->priv_data; yading@11: int64_t pos; yading@11: yading@11: if (stream_index) yading@11: return -1; yading@11: yading@11: pos = timestamp * yading@11: (tmv->audio_chunk_size + tmv->video_chunk_size + tmv->padding); yading@11: yading@11: if (avio_seek(s->pb, pos + TMV_HEADER_SIZE, SEEK_SET) < 0) yading@11: return -1; yading@11: tmv->stream_index = 0; yading@11: return 0; yading@11: } yading@11: yading@11: AVInputFormat ff_tmv_demuxer = { yading@11: .name = "tmv", yading@11: .long_name = NULL_IF_CONFIG_SMALL("8088flex TMV"), yading@11: .priv_data_size = sizeof(TMVContext), yading@11: .read_probe = tmv_probe, yading@11: .read_header = tmv_read_header, yading@11: .read_packet = tmv_read_packet, yading@11: .read_seek = tmv_read_seek, yading@11: .flags = AVFMT_GENERIC_INDEX, yading@11: };