Libav
|
00001 00026 #include "libavutil/intreadwrite.h" 00027 #include "avformat.h" 00028 00029 typedef struct yop_dec_context { 00030 AVPacket video_packet; 00031 00032 int odd_frame; 00033 int frame_size; 00034 int audio_block_length; 00035 int palette_size; 00036 } YopDecContext; 00037 00038 static int yop_probe(AVProbeData *probe_packet) 00039 { 00040 if (AV_RB16(probe_packet->buf) == AV_RB16("YO") && 00041 probe_packet->buf[6] && 00042 probe_packet->buf[7] && 00043 !(probe_packet->buf[8] & 1) && 00044 !(probe_packet->buf[10] & 1)) 00045 return AVPROBE_SCORE_MAX * 3 / 4; 00046 00047 return 0; 00048 } 00049 00050 static int yop_read_header(AVFormatContext *s, AVFormatParameters *ap) 00051 { 00052 YopDecContext *yop = s->priv_data; 00053 ByteIOContext *pb = s->pb; 00054 00055 AVCodecContext *audio_dec, *video_dec; 00056 AVStream *audio_stream, *video_stream; 00057 00058 int frame_rate, ret; 00059 00060 audio_stream = av_new_stream(s, 0); 00061 video_stream = av_new_stream(s, 1); 00062 00063 // Extra data that will be passed to the decoder 00064 video_stream->codec->extradata_size = 8; 00065 00066 video_stream->codec->extradata = av_mallocz(video_stream->codec->extradata_size + 00067 FF_INPUT_BUFFER_PADDING_SIZE); 00068 00069 if (!video_stream->codec->extradata) 00070 return AVERROR(ENOMEM); 00071 00072 // Audio 00073 audio_dec = audio_stream->codec; 00074 audio_dec->codec_type = AVMEDIA_TYPE_AUDIO; 00075 audio_dec->codec_id = CODEC_ID_ADPCM_IMA_WS; 00076 audio_dec->channels = 1; 00077 audio_dec->sample_rate = 22050; 00078 00079 // Video 00080 video_dec = video_stream->codec; 00081 video_dec->codec_type = AVMEDIA_TYPE_VIDEO; 00082 video_dec->codec_id = CODEC_ID_YOP; 00083 00084 url_fskip(pb, 6); 00085 00086 frame_rate = get_byte(pb); 00087 yop->frame_size = get_byte(pb) * 2048; 00088 video_dec->width = get_le16(pb); 00089 video_dec->height = get_le16(pb); 00090 00091 video_stream->sample_aspect_ratio = (AVRational){1, 2}; 00092 00093 ret = get_buffer(pb, video_dec->extradata, 8); 00094 if (ret < 8) 00095 return ret < 0 ? ret : AVERROR_EOF; 00096 00097 yop->palette_size = video_dec->extradata[0] * 3 + 4; 00098 yop->audio_block_length = AV_RL16(video_dec->extradata + 6); 00099 00100 // 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920 00101 if (yop->audio_block_length < 920 || 00102 yop->audio_block_length + yop->palette_size >= yop->frame_size) { 00103 av_log(s, AV_LOG_ERROR, "YOP has invalid header\n"); 00104 return AVERROR_INVALIDDATA; 00105 } 00106 00107 url_fseek(pb, 2048, SEEK_SET); 00108 00109 av_set_pts_info(video_stream, 32, 1, frame_rate); 00110 00111 return 0; 00112 } 00113 00114 static int yop_read_packet(AVFormatContext *s, AVPacket *pkt) 00115 { 00116 YopDecContext *yop = s->priv_data; 00117 ByteIOContext *pb = s->pb; 00118 00119 int ret; 00120 int actual_video_data_size = yop->frame_size - 00121 yop->audio_block_length - yop->palette_size; 00122 00123 yop->video_packet.stream_index = 1; 00124 00125 if (yop->video_packet.data) { 00126 *pkt = yop->video_packet; 00127 yop->video_packet.data = NULL; 00128 yop->video_packet.size = 0; 00129 pkt->data[0] = yop->odd_frame; 00130 pkt->flags |= AV_PKT_FLAG_KEY; 00131 yop->odd_frame ^= 1; 00132 return pkt->size; 00133 } 00134 ret = av_new_packet(&yop->video_packet, 00135 yop->frame_size - yop->audio_block_length); 00136 if (ret < 0) 00137 return ret; 00138 00139 yop->video_packet.pos = url_ftell(pb); 00140 00141 ret = get_buffer(pb, yop->video_packet.data, yop->palette_size); 00142 if (ret < 0) { 00143 goto err_out; 00144 }else if (ret < yop->palette_size) { 00145 ret = AVERROR_EOF; 00146 goto err_out; 00147 } 00148 00149 ret = av_get_packet(pb, pkt, 920); 00150 if (ret < 0) 00151 goto err_out; 00152 00153 // Set position to the start of the frame 00154 pkt->pos = yop->video_packet.pos; 00155 00156 url_fskip(pb, yop->audio_block_length - ret); 00157 00158 ret = get_buffer(pb, yop->video_packet.data + yop->palette_size, 00159 actual_video_data_size); 00160 if (ret < 0) 00161 goto err_out; 00162 else if (ret < actual_video_data_size) 00163 av_shrink_packet(&yop->video_packet, yop->palette_size + ret); 00164 00165 // Arbitrarily return the audio data first 00166 return yop->audio_block_length; 00167 00168 err_out: 00169 av_free_packet(&yop->video_packet); 00170 return ret; 00171 } 00172 00173 static int yop_read_close(AVFormatContext *s) 00174 { 00175 YopDecContext *yop = s->priv_data; 00176 av_free_packet(&yop->video_packet); 00177 return 0; 00178 } 00179 00180 static int yop_read_seek(AVFormatContext *s, int stream_index, 00181 int64_t timestamp, int flags) 00182 { 00183 YopDecContext *yop = s->priv_data; 00184 int64_t frame_pos, pos_min, pos_max; 00185 int frame_count; 00186 00187 av_free_packet(&yop->video_packet); 00188 00189 if (!stream_index) 00190 return -1; 00191 00192 pos_min = s->data_offset; 00193 pos_max = url_fsize(s->pb) - yop->frame_size; 00194 frame_count = (pos_max - pos_min) / yop->frame_size; 00195 00196 timestamp = FFMAX(0, FFMIN(frame_count, timestamp)); 00197 00198 frame_pos = timestamp * yop->frame_size + pos_min; 00199 yop->odd_frame = timestamp & 1; 00200 00201 url_fseek(s->pb, frame_pos, SEEK_SET); 00202 return 0; 00203 } 00204 00205 AVInputFormat yop_demuxer = { 00206 "yop", 00207 NULL_IF_CONFIG_SMALL("Psygnosis YOP Format"), 00208 sizeof(YopDecContext), 00209 yop_probe, 00210 yop_read_header, 00211 yop_read_packet, 00212 yop_read_close, 00213 yop_read_seek, 00214 .extensions = "yop", 00215 .flags = AVFMT_GENERIC_INDEX, 00216 };