1/* 2 * TechnoTrend PVA (.pva) demuxer 3 * Copyright (c) 2007, 2008 Ivo van Poorten 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 "avformat.h" 23#include "mpeg.h" 24 25#define PVA_MAX_PAYLOAD_LENGTH 0x17f8 26#define PVA_VIDEO_PAYLOAD 0x01 27#define PVA_AUDIO_PAYLOAD 0x02 28#define PVA_MAGIC (('A' << 8) + 'V') 29 30typedef struct { 31 int continue_pes; 32} PVAContext; 33 34static int pva_probe(AVProbeData * pd) { 35 unsigned char *buf = pd->buf; 36 37 if (AV_RB16(buf) == PVA_MAGIC && buf[2] && buf[2] < 3 && buf[4] == 0x55) 38 return AVPROBE_SCORE_MAX / 2; 39 40 return 0; 41} 42 43static int pva_read_header(AVFormatContext *s, AVFormatParameters *ap) { 44 AVStream *st; 45 46 if (!(st = av_new_stream(s, 0))) 47 return AVERROR(ENOMEM); 48 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 49 st->codec->codec_id = CODEC_ID_MPEG2VIDEO; 50 st->need_parsing = AVSTREAM_PARSE_FULL; 51 av_set_pts_info(st, 32, 1, 90000); 52 av_add_index_entry(st, 0, 0, 0, 0, AVINDEX_KEYFRAME); 53 54 if (!(st = av_new_stream(s, 1))) 55 return AVERROR(ENOMEM); 56 st->codec->codec_type = AVMEDIA_TYPE_AUDIO; 57 st->codec->codec_id = CODEC_ID_MP2; 58 st->need_parsing = AVSTREAM_PARSE_FULL; 59 av_set_pts_info(st, 33, 1, 90000); 60 av_add_index_entry(st, 0, 0, 0, 0, AVINDEX_KEYFRAME); 61 62 /* the parameters will be extracted from the compressed bitstream */ 63 return 0; 64} 65 66#define pva_log if (read_packet) av_log 67 68static int read_part_of_packet(AVFormatContext *s, int64_t *pts, 69 int *len, int *strid, int read_packet) { 70 ByteIOContext *pb = s->pb; 71 PVAContext *pvactx = s->priv_data; 72 int syncword, streamid, reserved, flags, length, pts_flag; 73 int64_t pva_pts = AV_NOPTS_VALUE, startpos; 74 75recover: 76 startpos = url_ftell(pb); 77 78 syncword = get_be16(pb); 79 streamid = get_byte(pb); 80 get_byte(pb); /* counter not used */ 81 reserved = get_byte(pb); 82 flags = get_byte(pb); 83 length = get_be16(pb); 84 85 pts_flag = flags & 0x10; 86 87 if (syncword != PVA_MAGIC) { 88 pva_log(s, AV_LOG_ERROR, "invalid syncword\n"); 89 return AVERROR(EIO); 90 } 91 if (streamid != PVA_VIDEO_PAYLOAD && streamid != PVA_AUDIO_PAYLOAD) { 92 pva_log(s, AV_LOG_ERROR, "invalid streamid\n"); 93 return AVERROR(EIO); 94 } 95 if (reserved != 0x55) { 96 pva_log(s, AV_LOG_WARNING, "expected reserved byte to be 0x55\n"); 97 } 98 if (length > PVA_MAX_PAYLOAD_LENGTH) { 99 pva_log(s, AV_LOG_ERROR, "invalid payload length %u\n", length); 100 return AVERROR(EIO); 101 } 102 103 if (streamid == PVA_VIDEO_PAYLOAD && pts_flag) { 104 pva_pts = get_be32(pb); 105 length -= 4; 106 } else if (streamid == PVA_AUDIO_PAYLOAD) { 107 /* PVA Audio Packets either start with a signaled PES packet or 108 * are a continuation of the previous PES packet. New PES packets 109 * always start at the beginning of a PVA Packet, never somewhere in 110 * the middle. */ 111 if (!pvactx->continue_pes) { 112 int pes_signal, pes_header_data_length, pes_packet_length, 113 pes_flags; 114 unsigned char pes_header_data[256]; 115 116 pes_signal = get_be24(pb); 117 get_byte(pb); 118 pes_packet_length = get_be16(pb); 119 pes_flags = get_be16(pb); 120 pes_header_data_length = get_byte(pb); 121 122 if (pes_signal != 1) { 123 pva_log(s, AV_LOG_WARNING, "expected signaled PES packet, " 124 "trying to recover\n"); 125 url_fskip(pb, length - 9); 126 if (!read_packet) 127 return AVERROR(EIO); 128 goto recover; 129 } 130 131 get_buffer(pb, pes_header_data, pes_header_data_length); 132 length -= 9 + pes_header_data_length; 133 134 pes_packet_length -= 3 + pes_header_data_length; 135 136 pvactx->continue_pes = pes_packet_length; 137 138 if (pes_flags & 0x80 && (pes_header_data[0] & 0xf0) == 0x20) 139 pva_pts = ff_parse_pes_pts(pes_header_data); 140 } 141 142 pvactx->continue_pes -= length; 143 144 if (pvactx->continue_pes < 0) { 145 pva_log(s, AV_LOG_WARNING, "audio data corruption\n"); 146 pvactx->continue_pes = 0; 147 } 148 } 149 150 if (pva_pts != AV_NOPTS_VALUE) 151 av_add_index_entry(s->streams[streamid-1], startpos, pva_pts, 0, 0, AVINDEX_KEYFRAME); 152 153 *pts = pva_pts; 154 *len = length; 155 *strid = streamid; 156 return 0; 157} 158 159static int pva_read_packet(AVFormatContext *s, AVPacket *pkt) { 160 ByteIOContext *pb = s->pb; 161 int64_t pva_pts; 162 int ret, length, streamid; 163 164 if (read_part_of_packet(s, &pva_pts, &length, &streamid, 1) < 0 || 165 (ret = av_get_packet(pb, pkt, length)) <= 0) 166 return AVERROR(EIO); 167 168 pkt->stream_index = streamid - 1; 169 pkt->pts = pva_pts; 170 171 return ret; 172} 173 174static int64_t pva_read_timestamp(struct AVFormatContext *s, int stream_index, 175 int64_t *pos, int64_t pos_limit) { 176 ByteIOContext *pb = s->pb; 177 PVAContext *pvactx = s->priv_data; 178 int length, streamid; 179 int64_t res = AV_NOPTS_VALUE; 180 181 pos_limit = FFMIN(*pos+PVA_MAX_PAYLOAD_LENGTH*8, (uint64_t)*pos+pos_limit); 182 183 while (*pos < pos_limit) { 184 res = AV_NOPTS_VALUE; 185 url_fseek(pb, *pos, SEEK_SET); 186 187 pvactx->continue_pes = 0; 188 if (read_part_of_packet(s, &res, &length, &streamid, 0)) { 189 (*pos)++; 190 continue; 191 } 192 if (streamid - 1 != stream_index || res == AV_NOPTS_VALUE) { 193 *pos = url_ftell(pb) + length; 194 continue; 195 } 196 break; 197 } 198 199 pvactx->continue_pes = 0; 200 return res; 201} 202 203AVInputFormat pva_demuxer = { 204 "pva", 205 NULL_IF_CONFIG_SMALL("TechnoTrend PVA file and stream format"), 206 sizeof(PVAContext), 207 pva_probe, 208 pva_read_header, 209 pva_read_packet, 210 .read_timestamp = pva_read_timestamp 211}; 212