1/* 2 * Psygnosis YOP demuxer 3 * 4 * Copyright (C) 2010 Mohamed Naufal Basheer <naufal11@gmail.com> 5 * derived from the code by 6 * Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com> 7 * 8 * This file is part of Libav. 9 * 10 * Libav is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * Libav is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with Libav; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25#include "libavutil/intreadwrite.h" 26#include "avformat.h" 27#include "internal.h" 28 29typedef struct yop_dec_context { 30 AVPacket video_packet; 31 32 int odd_frame; 33 int frame_size; 34 int audio_block_length; 35 int palette_size; 36} YopDecContext; 37 38static int yop_probe(AVProbeData *probe_packet) 39{ 40 if (AV_RB16(probe_packet->buf) == AV_RB16("YO") && 41 probe_packet->buf[6] && 42 probe_packet->buf[7] && 43 !(probe_packet->buf[8] & 1) && 44 !(probe_packet->buf[10] & 1)) 45 return AVPROBE_SCORE_MAX * 3 / 4; 46 47 return 0; 48} 49 50static int yop_read_header(AVFormatContext *s, AVFormatParameters *ap) 51{ 52 YopDecContext *yop = s->priv_data; 53 AVIOContext *pb = s->pb; 54 55 AVCodecContext *audio_dec, *video_dec; 56 AVStream *audio_stream, *video_stream; 57 58 int frame_rate, ret; 59 60 audio_stream = avformat_new_stream(s, NULL); 61 video_stream = avformat_new_stream(s, NULL); 62 63 // Extra data that will be passed to the decoder 64 video_stream->codec->extradata_size = 8; 65 66 video_stream->codec->extradata = av_mallocz(video_stream->codec->extradata_size + 67 FF_INPUT_BUFFER_PADDING_SIZE); 68 69 if (!video_stream->codec->extradata) 70 return AVERROR(ENOMEM); 71 72 // Audio 73 audio_dec = audio_stream->codec; 74 audio_dec->codec_type = AVMEDIA_TYPE_AUDIO; 75 audio_dec->codec_id = CODEC_ID_ADPCM_IMA_WS; 76 audio_dec->channels = 1; 77 audio_dec->sample_rate = 22050; 78 79 // Video 80 video_dec = video_stream->codec; 81 video_dec->codec_type = AVMEDIA_TYPE_VIDEO; 82 video_dec->codec_id = CODEC_ID_YOP; 83 84 avio_skip(pb, 6); 85 86 frame_rate = avio_r8(pb); 87 yop->frame_size = avio_r8(pb) * 2048; 88 video_dec->width = avio_rl16(pb); 89 video_dec->height = avio_rl16(pb); 90 91 video_stream->sample_aspect_ratio = (AVRational){1, 2}; 92 93 ret = avio_read(pb, video_dec->extradata, 8); 94 if (ret < 8) 95 return ret < 0 ? ret : AVERROR_EOF; 96 97 yop->palette_size = video_dec->extradata[0] * 3 + 4; 98 yop->audio_block_length = AV_RL16(video_dec->extradata + 6); 99 100 // 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920 101 if (yop->audio_block_length < 920 || 102 yop->audio_block_length + yop->palette_size >= yop->frame_size) { 103 av_log(s, AV_LOG_ERROR, "YOP has invalid header\n"); 104 return AVERROR_INVALIDDATA; 105 } 106 107 avio_seek(pb, 2048, SEEK_SET); 108 109 avpriv_set_pts_info(video_stream, 32, 1, frame_rate); 110 111 return 0; 112} 113 114static int yop_read_packet(AVFormatContext *s, AVPacket *pkt) 115{ 116 YopDecContext *yop = s->priv_data; 117 AVIOContext *pb = s->pb; 118 119 int ret; 120 int actual_video_data_size = yop->frame_size - 121 yop->audio_block_length - yop->palette_size; 122 123 yop->video_packet.stream_index = 1; 124 125 if (yop->video_packet.data) { 126 *pkt = yop->video_packet; 127 yop->video_packet.data = NULL; 128 yop->video_packet.size = 0; 129 pkt->data[0] = yop->odd_frame; 130 pkt->flags |= AV_PKT_FLAG_KEY; 131 yop->odd_frame ^= 1; 132 return pkt->size; 133 } 134 ret = av_new_packet(&yop->video_packet, 135 yop->frame_size - yop->audio_block_length); 136 if (ret < 0) 137 return ret; 138 139 yop->video_packet.pos = avio_tell(pb); 140 141 ret = avio_read(pb, yop->video_packet.data, yop->palette_size); 142 if (ret < 0) { 143 goto err_out; 144 }else if (ret < yop->palette_size) { 145 ret = AVERROR_EOF; 146 goto err_out; 147 } 148 149 ret = av_get_packet(pb, pkt, 920); 150 if (ret < 0) 151 goto err_out; 152 153 // Set position to the start of the frame 154 pkt->pos = yop->video_packet.pos; 155 156 avio_skip(pb, yop->audio_block_length - ret); 157 158 ret = avio_read(pb, yop->video_packet.data + yop->palette_size, 159 actual_video_data_size); 160 if (ret < 0) 161 goto err_out; 162 else if (ret < actual_video_data_size) 163 av_shrink_packet(&yop->video_packet, yop->palette_size + ret); 164 165 // Arbitrarily return the audio data first 166 return yop->audio_block_length; 167 168err_out: 169 av_free_packet(&yop->video_packet); 170 return ret; 171} 172 173static int yop_read_close(AVFormatContext *s) 174{ 175 YopDecContext *yop = s->priv_data; 176 av_free_packet(&yop->video_packet); 177 return 0; 178} 179 180static int yop_read_seek(AVFormatContext *s, int stream_index, 181 int64_t timestamp, int flags) 182{ 183 YopDecContext *yop = s->priv_data; 184 int64_t frame_pos, pos_min, pos_max; 185 int frame_count; 186 187 av_free_packet(&yop->video_packet); 188 189 if (!stream_index) 190 return -1; 191 192 pos_min = s->data_offset; 193 pos_max = avio_size(s->pb) - yop->frame_size; 194 frame_count = (pos_max - pos_min) / yop->frame_size; 195 196 timestamp = FFMAX(0, FFMIN(frame_count, timestamp)); 197 198 frame_pos = timestamp * yop->frame_size + pos_min; 199 yop->odd_frame = timestamp & 1; 200 201 avio_seek(s->pb, frame_pos, SEEK_SET); 202 return 0; 203} 204 205AVInputFormat ff_yop_demuxer = { 206 .name = "yop", 207 .long_name = NULL_IF_CONFIG_SMALL("Psygnosis YOP Format"), 208 .priv_data_size = sizeof(YopDecContext), 209 .read_probe = yop_probe, 210 .read_header = yop_read_header, 211 .read_packet = yop_read_packet, 212 .read_close = yop_read_close, 213 .read_seek = yop_read_seek, 214 .extensions = "yop", 215 .flags = AVFMT_GENERIC_INDEX, 216}; 217