1/* 2 * THP Demuxer 3 * Copyright (c) 2007 Marco Gerards 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#include "libavutil/intreadwrite.h" 23#include "libavutil/intfloat.h" 24#include "avformat.h" 25#include "avio_internal.h" 26#include "internal.h" 27 28typedef struct ThpDemuxContext { 29 int version; 30 unsigned first_frame; 31 unsigned first_framesz; 32 unsigned last_frame; 33 int compoff; 34 unsigned framecnt; 35 AVRational fps; 36 unsigned frame; 37 int64_t next_frame; 38 unsigned next_framesz; 39 int video_stream_index; 40 int audio_stream_index; 41 int compcount; 42 unsigned char components[16]; 43 AVStream* vst; 44 int has_audio; 45 unsigned audiosize; 46} ThpDemuxContext; 47 48 49static int thp_probe(const AVProbeData *p) 50{ 51 double d; 52 /* check file header */ 53 if (AV_RL32(p->buf) != MKTAG('T', 'H', 'P', '\0')) 54 return 0; 55 56 d = av_int2float(AV_RB32(p->buf + 16)); 57 if (d < 0.1 || d > 1000 || isnan(d)) 58 return AVPROBE_SCORE_MAX/4; 59 60 return AVPROBE_SCORE_MAX; 61} 62 63static int thp_read_header(AVFormatContext *s) 64{ 65 ThpDemuxContext *thp = s->priv_data; 66 AVStream *st; 67 AVIOContext *pb = s->pb; 68 int64_t fsize= avio_size(pb); 69 uint32_t maxsize; 70 int i; 71 72 /* Read the file header. */ 73 avio_rb32(pb); /* Skip Magic. */ 74 thp->version = avio_rb32(pb); 75 76 avio_rb32(pb); /* Max buf size. */ 77 avio_rb32(pb); /* Max samples. */ 78 79 thp->fps = av_d2q(av_int2float(avio_rb32(pb)), INT_MAX); 80 if (thp->fps.den <= 0 || thp->fps.num < 0) 81 return AVERROR_INVALIDDATA; 82 thp->framecnt = avio_rb32(pb); 83 thp->first_framesz = avio_rb32(pb); 84 maxsize = avio_rb32(pb); 85 if (fsize > 0 && (!maxsize || fsize < maxsize)) 86 maxsize = fsize; 87 ffiocontext(pb)->maxsize = fsize; 88 89 thp->compoff = avio_rb32(pb); 90 avio_rb32(pb); /* offsetDataOffset. */ 91 thp->first_frame = avio_rb32(pb); 92 thp->last_frame = avio_rb32(pb); 93 94 thp->next_framesz = thp->first_framesz; 95 thp->next_frame = thp->first_frame; 96 97 /* Read the component structure. */ 98 avio_seek (pb, thp->compoff, SEEK_SET); 99 thp->compcount = avio_rb32(pb); 100 101 if (thp->compcount > FF_ARRAY_ELEMS(thp->components)) 102 return AVERROR_INVALIDDATA; 103 104 /* Read the list of component types. */ 105 avio_read(pb, thp->components, 16); 106 107 for (i = 0; i < thp->compcount; i++) { 108 if (thp->components[i] == 0) { 109 if (thp->vst) 110 break; 111 112 /* Video component. */ 113 st = avformat_new_stream(s, NULL); 114 if (!st) 115 return AVERROR(ENOMEM); 116 117 /* The denominator and numerator are switched because 1/fps 118 is required. */ 119 avpriv_set_pts_info(st, 64, thp->fps.den, thp->fps.num); 120 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; 121 st->codecpar->codec_id = AV_CODEC_ID_THP; 122 st->codecpar->codec_tag = 0; /* no fourcc */ 123 st->codecpar->width = avio_rb32(pb); 124 st->codecpar->height = avio_rb32(pb); 125 st->codecpar->sample_rate = av_q2d(thp->fps); 126 st->nb_frames = 127 st->duration = thp->framecnt; 128 thp->vst = st; 129 thp->video_stream_index = st->index; 130 131 if (thp->version == 0x11000) 132 avio_rb32(pb); /* Unknown. */ 133 } else if (thp->components[i] == 1) { 134 if (thp->has_audio != 0) 135 break; 136 137 /* Audio component. */ 138 st = avformat_new_stream(s, NULL); 139 if (!st) 140 return AVERROR(ENOMEM); 141 142 st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; 143 st->codecpar->codec_id = AV_CODEC_ID_ADPCM_THP; 144 st->codecpar->codec_tag = 0; /* no fourcc */ 145 st->codecpar->ch_layout.nb_channels = avio_rb32(pb); 146 st->codecpar->sample_rate = avio_rb32(pb); /* Frequency. */ 147 st->duration = avio_rb32(pb); 148 149 avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); 150 151 thp->audio_stream_index = st->index; 152 thp->has_audio = 1; 153 } 154 } 155 156 if (!thp->vst) 157 return AVERROR_INVALIDDATA; 158 159 return 0; 160} 161 162static int thp_read_packet(AVFormatContext *s, 163 AVPacket *pkt) 164{ 165 ThpDemuxContext *thp = s->priv_data; 166 AVIOContext *pb = s->pb; 167 unsigned int size; 168 int ret; 169 170 if (thp->audiosize == 0) { 171 /* Terminate when last frame is reached. */ 172 if (thp->frame >= thp->framecnt) 173 return AVERROR_EOF; 174 175 avio_seek(pb, thp->next_frame, SEEK_SET); 176 177 /* Locate the next frame and read out its size. */ 178 thp->next_frame += FFMAX(thp->next_framesz, 1); 179 thp->next_framesz = avio_rb32(pb); 180 181 avio_rb32(pb); /* Previous total size. */ 182 size = avio_rb32(pb); /* Total size of this frame. */ 183 184 /* Store the audiosize so the next time this function is called, 185 the audio can be read. */ 186 if (thp->has_audio) 187 thp->audiosize = avio_rb32(pb); /* Audio size. */ 188 else 189 thp->frame++; 190 191 ret = av_get_packet(pb, pkt, size); 192 if (ret < 0) 193 return ret; 194 if (ret != size) { 195 return AVERROR(EIO); 196 } 197 198 pkt->stream_index = thp->video_stream_index; 199 } else { 200 ret = av_get_packet(pb, pkt, thp->audiosize); 201 if (ret < 0) 202 return ret; 203 if (ret != thp->audiosize) { 204 return AVERROR(EIO); 205 } 206 207 pkt->stream_index = thp->audio_stream_index; 208 if (thp->audiosize >= 8) 209 pkt->duration = AV_RB32(&pkt->data[4]); 210 211 thp->audiosize = 0; 212 thp->frame++; 213 } 214 215 return 0; 216} 217 218const AVInputFormat ff_thp_demuxer = { 219 .name = "thp", 220 .long_name = NULL_IF_CONFIG_SMALL("THP"), 221 .priv_data_size = sizeof(ThpDemuxContext), 222 .read_probe = thp_probe, 223 .read_header = thp_read_header, 224 .read_packet = thp_read_packet 225}; 226