Patch by Mohamed Naufal gmailify(naufal11). Originally committed as revision 22724 to svn://svn.ffmpeg.org/ffmpeg/trunktags/v0.6
| @@ -241,6 +241,7 @@ library: | |||
| @item Westwood Studios VQA @tab @tab X | |||
| @tab Multimedia format used in Westwood Studios games. | |||
| @item YUV4MPEG pipe @tab X @tab X | |||
| @item Psygnosis YOP @tab @tab X | |||
| @end multitable | |||
| @code{X} means that encoding (resp. decoding) is supported. | |||
| @@ -490,6 +491,7 @@ following image formats are supported: | |||
| @item Winnov WNV1 @tab @tab X | |||
| @item WMV7 @tab X @tab X | |||
| @item YAMAHA SMAF @tab X @tab X | |||
| @item Psygnosis YOP Video @tab @tab X | |||
| @item ZLIB @tab X @tab X | |||
| @tab part of LCL, encoder experimental | |||
| @item Zip Motion Blocks Video @tab X @tab X | |||
| @@ -386,6 +386,7 @@ OBJS-$(CONFIG_XAN_WC4_DECODER) += xan.o | |||
| OBJS-$(CONFIG_XL_DECODER) += xl.o | |||
| OBJS-$(CONFIG_XSUB_DECODER) += xsubdec.o | |||
| OBJS-$(CONFIG_XSUB_ENCODER) += xsubenc.o | |||
| OBJS-$(CONFIG_YOP_DECODER) += yop.o | |||
| OBJS-$(CONFIG_ZLIB_DECODER) += lcldec.o | |||
| OBJS-$(CONFIG_ZLIB_ENCODER) += lclenc.o | |||
| OBJS-$(CONFIG_ZMBV_DECODER) += zmbv.o | |||
| @@ -207,6 +207,7 @@ void avcodec_register_all(void) | |||
| REGISTER_DECODER (WNV1, wnv1); | |||
| REGISTER_DECODER (XAN_WC3, xan_wc3); | |||
| REGISTER_DECODER (XL, xl); | |||
| REGISTER_DECODER (YOP, yop); | |||
| REGISTER_ENCDEC (ZLIB, zlib); | |||
| REGISTER_ENCDEC (ZMBV, zmbv); | |||
| @@ -30,7 +30,7 @@ | |||
| #include "libavutil/avutil.h" | |||
| #define LIBAVCODEC_VERSION_MAJOR 52 | |||
| #define LIBAVCODEC_VERSION_MINOR 61 | |||
| #define LIBAVCODEC_VERSION_MINOR 62 | |||
| #define LIBAVCODEC_VERSION_MICRO 0 | |||
| #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ | |||
| @@ -209,6 +209,7 @@ enum CodecID { | |||
| CODEC_ID_IFF_ILBM, | |||
| CODEC_ID_IFF_BYTERUN1, | |||
| CODEC_ID_KGV1, | |||
| CODEC_ID_YOP, | |||
| /* various PCM "codecs" */ | |||
| CODEC_ID_PCM_S16LE= 0x10000, | |||
| @@ -0,0 +1,260 @@ | |||
| /** | |||
| * @file libavcodec/yop.c | |||
| * Psygnosis YOP decoder | |||
| * | |||
| * Copyright (C) 2010 Mohamed Naufal Basheer <naufal11@gmail.com> | |||
| * derived from the code by | |||
| * Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com> | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "libavutil/intreadwrite.h" | |||
| #include "avcodec.h" | |||
| #include "get_bits.h" | |||
| typedef struct YopDecContext { | |||
| AVFrame frame; | |||
| AVCodecContext *avctx; | |||
| int num_pal_colors; | |||
| int first_color[2]; | |||
| int frame_data_length; | |||
| int row_pos; | |||
| uint8_t *low_nibble; | |||
| uint8_t *srcptr; | |||
| uint8_t *dstptr; | |||
| uint8_t *dstbuf; | |||
| } YopDecContext; | |||
| // These tables are taken directly from: | |||
| // http://wiki.multimedia.cx/index.php?title=Psygnosis_YOP | |||
| /** | |||
| * Lookup table for painting macroblocks. Bytes 0-2 of each entry contain | |||
| * the macroblock positions to be painted (taken as (0, B0, B1, B2)). | |||
| * Byte 3 contains the number of bytes consumed on the input, | |||
| * equal to max(bytes 0-2) + 1. | |||
| */ | |||
| static const uint8_t paint_lut[15][4] = | |||
| {{1, 2, 3, 4}, {1, 2, 0, 3}, | |||
| {1, 2, 1, 3}, {1, 2, 2, 3}, | |||
| {1, 0, 2, 3}, {1, 0, 0, 2}, | |||
| {1, 0, 1, 2}, {1, 1, 2, 3}, | |||
| {0, 1, 2, 3}, {0, 1, 0, 2}, | |||
| {1, 1, 0, 2}, {0, 1, 1, 2}, | |||
| {0, 0, 1, 2}, {0, 0, 0, 1}, | |||
| {1, 1, 1, 2}, | |||
| }; | |||
| /** | |||
| * Lookup table for copying macroblocks. Each entry contains the respective | |||
| * x and y pixel offset for the copy source. | |||
| */ | |||
| static const int8_t motion_vector[16][2] = | |||
| {{-4, -4}, {-2, -4}, | |||
| { 0, -4}, { 2, -4}, | |||
| {-4, -2}, {-4, 0}, | |||
| {-3, -3}, {-1, -3}, | |||
| { 1, -3}, { 3, -3}, | |||
| {-3, -1}, {-2, -2}, | |||
| { 0, -2}, { 2, -2}, | |||
| { 4, -2}, {-2, 0}, | |||
| }; | |||
| static av_cold int yop_decode_init(AVCodecContext *avctx) | |||
| { | |||
| YopDecContext *s = avctx->priv_data; | |||
| s->avctx = avctx; | |||
| if (avctx->width & 1 || avctx->height & 1 || | |||
| avcodec_check_dimensions(avctx, avctx->width, avctx->height) < 0) { | |||
| av_log(avctx, AV_LOG_ERROR, "YOP has invalid dimensions\n"); | |||
| return -1; | |||
| } | |||
| avctx->pix_fmt = PIX_FMT_PAL8; | |||
| s->num_pal_colors = avctx->extradata[0]; | |||
| s->first_color[0] = avctx->extradata[1]; | |||
| s->first_color[1] = avctx->extradata[2]; | |||
| if (s->num_pal_colors + s->first_color[0] > 256 || | |||
| s->num_pal_colors + s->first_color[1] > 256) { | |||
| av_log(avctx, AV_LOG_ERROR, | |||
| "YOP: palette parameters invalid, header probably corrupt\n"); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| return 0; | |||
| } | |||
| static av_cold int yop_decode_close(AVCodecContext *avctx) | |||
| { | |||
| YopDecContext *s = avctx->priv_data; | |||
| if (s->frame.data[0]) | |||
| avctx->release_buffer(avctx, &s->frame); | |||
| return 0; | |||
| } | |||
| /** | |||
| * Paints a macroblock using the pattern in paint_lut. | |||
| * @param s codec context | |||
| * @param tag the tag that was in the nibble | |||
| */ | |||
| static void yop_paint_block(YopDecContext *s, int tag) | |||
| { | |||
| s->dstptr[0] = s->srcptr[0]; | |||
| s->dstptr[1] = s->srcptr[paint_lut[tag][0]]; | |||
| s->dstptr[s->frame.linesize[0]] = s->srcptr[paint_lut[tag][1]]; | |||
| s->dstptr[s->frame.linesize[0] + 1] = s->srcptr[paint_lut[tag][2]]; | |||
| // The number of src bytes consumed is in the last part of the lut entry. | |||
| s->srcptr += paint_lut[tag][3]; | |||
| } | |||
| /** | |||
| * Copies a previously painted macroblock to the current_block. | |||
| * @param copy_tag the tag that was in the nibble | |||
| */ | |||
| static int yop_copy_previous_block(YopDecContext *s, int copy_tag) | |||
| { | |||
| uint8_t *bufptr; | |||
| // Calculate position for the copy source | |||
| bufptr = s->dstptr + motion_vector[copy_tag][0] + | |||
| s->frame.linesize[0] * motion_vector[copy_tag][1]; | |||
| if (bufptr < s->dstbuf) { | |||
| av_log(s->avctx, AV_LOG_ERROR, | |||
| "YOP: cannot decode, file probably corrupt\n"); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| s->dstptr[0] = bufptr[0]; | |||
| s->dstptr[1] = bufptr[1]; | |||
| s->dstptr[s->frame.linesize[0]] = bufptr[s->frame.linesize[0]]; | |||
| s->dstptr[s->frame.linesize[0] + 1] = bufptr[s->frame.linesize[0] + 1]; | |||
| return 0; | |||
| } | |||
| /** | |||
| * Returns the next nibble in sequence, consuming a new byte on the input | |||
| * only if necessary. | |||
| */ | |||
| static uint8_t yop_get_next_nibble(YopDecContext *s) | |||
| { | |||
| int ret; | |||
| if (s->low_nibble) { | |||
| ret = *s->low_nibble & 0xf; | |||
| s->low_nibble = NULL; | |||
| }else { | |||
| s->low_nibble = s->srcptr++; | |||
| ret = *s->low_nibble >> 4; | |||
| } | |||
| return ret; | |||
| } | |||
| /** | |||
| * Takes s->dstptr to the next macroblock in sequence. | |||
| */ | |||
| static void yop_next_macroblock(YopDecContext *s) | |||
| { | |||
| // If we are advancing to the next row of macroblocks | |||
| if (s->row_pos == s->frame.linesize[0] - 2) { | |||
| s->dstptr += s->frame.linesize[0]; | |||
| s->row_pos = 0; | |||
| }else { | |||
| s->row_pos += 2; | |||
| } | |||
| s->dstptr += 2; | |||
| } | |||
| static int yop_decode_frame(AVCodecContext *avctx, void *data, int *data_size, | |||
| AVPacket *avpkt) | |||
| { | |||
| YopDecContext *s = avctx->priv_data; | |||
| int tag, firstcolor, is_odd_frame; | |||
| int ret, i; | |||
| uint32_t *palette; | |||
| if (s->frame.data[0]) | |||
| avctx->release_buffer(avctx, &s->frame); | |||
| ret = avctx->get_buffer(avctx, &s->frame); | |||
| if (ret < 0) { | |||
| av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); | |||
| return ret; | |||
| } | |||
| s->frame.linesize[0] = avctx->width; | |||
| s->dstbuf = s->frame.data[0]; | |||
| s->dstptr = s->frame.data[0]; | |||
| s->srcptr = avpkt->data + 4; | |||
| s->row_pos = 0; | |||
| s->low_nibble = NULL; | |||
| is_odd_frame = avpkt->data[0]; | |||
| firstcolor = s->first_color[is_odd_frame]; | |||
| palette = (uint32_t *)s->frame.data[1]; | |||
| for (i = 0; i < s->num_pal_colors; i++, s->srcptr += 3) | |||
| palette[i + firstcolor] = (s->srcptr[0] << 18) | | |||
| (s->srcptr[1] << 10) | | |||
| (s->srcptr[2] << 2); | |||
| s->frame.palette_has_changed = 1; | |||
| while (s->dstptr - s->dstbuf < | |||
| avctx->width * avctx->height && | |||
| s->srcptr - avpkt->data < avpkt->size) { | |||
| tag = yop_get_next_nibble(s); | |||
| if (tag != 0xf) { | |||
| yop_paint_block(s, tag); | |||
| }else { | |||
| tag = yop_get_next_nibble(s); | |||
| ret = yop_copy_previous_block(s, tag); | |||
| if (ret < 0) { | |||
| avctx->release_buffer(avctx, &s->frame); | |||
| return ret; | |||
| } | |||
| } | |||
| yop_next_macroblock(s); | |||
| } | |||
| *data_size = sizeof(AVFrame); | |||
| *(AVFrame *) data = s->frame; | |||
| return avpkt->size; | |||
| } | |||
| AVCodec yop_decoder = { | |||
| "yop", | |||
| CODEC_TYPE_VIDEO, | |||
| CODEC_ID_YOP, | |||
| sizeof(YopDecContext), | |||
| yop_decode_init, | |||
| NULL, | |||
| yop_decode_close, | |||
| yop_decode_frame, | |||
| .long_name = NULL_IF_CONFIG_SMALL("Psygnosis YOP Video"), | |||
| }; | |||
| @@ -260,6 +260,7 @@ OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood.o | |||
| OBJS-$(CONFIG_WSVQA_DEMUXER) += westwood.o | |||
| OBJS-$(CONFIG_WV_DEMUXER) += wv.o apetag.o id3v1.o | |||
| OBJS-$(CONFIG_XA_DEMUXER) += xa.o | |||
| OBJS-$(CONFIG_YOP_DEMUXER) += yop.o | |||
| OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER) += yuv4mpeg.o | |||
| OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER) += yuv4mpeg.o | |||
| @@ -207,6 +207,7 @@ void av_register_all(void) | |||
| REGISTER_DEMUXER (WSVQA, wsvqa); | |||
| REGISTER_DEMUXER (WV, wv); | |||
| REGISTER_DEMUXER (XA, xa); | |||
| REGISTER_DEMUXER (YOP, yop); | |||
| REGISTER_MUXDEMUX (YUV4MPEGPIPE, yuv4mpegpipe); | |||
| /* external libraries */ | |||
| @@ -22,7 +22,7 @@ | |||
| #define AVFORMAT_AVFORMAT_H | |||
| #define LIBAVFORMAT_VERSION_MAJOR 52 | |||
| #define LIBAVFORMAT_VERSION_MINOR 58 | |||
| #define LIBAVFORMAT_VERSION_MINOR 59 | |||
| #define LIBAVFORMAT_VERSION_MICRO 0 | |||
| #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ | |||
| @@ -0,0 +1,216 @@ | |||
| /** | |||
| * @file libavformat/yop.c | |||
| * Psygnosis YOP demuxer | |||
| * | |||
| * Copyright (C) 2010 Mohamed Naufal Basheer <naufal11@gmail.com> | |||
| * derived from the code by | |||
| * Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com> | |||
| * | |||
| * This file is part of FFmpeg. | |||
| * | |||
| * FFmpeg is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * FFmpeg is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with FFmpeg; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #include "libavutil/intreadwrite.h" | |||
| #include "avformat.h" | |||
| typedef struct yop_dec_context { | |||
| AVPacket video_packet; | |||
| int odd_frame; | |||
| int frame_size; | |||
| int audio_block_length; | |||
| int palette_size; | |||
| } YopDecContext; | |||
| static int yop_probe(AVProbeData *probe_packet) | |||
| { | |||
| if (AV_RB16(probe_packet->buf) == AV_RB16("YO") && | |||
| probe_packet->buf[6] && | |||
| probe_packet->buf[7] && | |||
| !(probe_packet->buf[8] & 1) && | |||
| !(probe_packet->buf[10] & 1)) | |||
| return AVPROBE_SCORE_MAX * 3 / 4; | |||
| return 0; | |||
| } | |||
| static int yop_read_header(AVFormatContext *s, AVFormatParameters *ap) | |||
| { | |||
| YopDecContext *yop = s->priv_data; | |||
| ByteIOContext *pb = s->pb; | |||
| AVCodecContext *audio_dec, *video_dec; | |||
| AVStream *audio_stream, *video_stream; | |||
| int frame_rate, ret; | |||
| audio_stream = av_new_stream(s, 0); | |||
| video_stream = av_new_stream(s, 1); | |||
| // Extra data that will be passed to the decoder | |||
| video_stream->codec->extradata_size = 8; | |||
| video_stream->codec->extradata = av_mallocz(video_stream->codec->extradata_size + | |||
| FF_INPUT_BUFFER_PADDING_SIZE); | |||
| if (!video_stream->codec->extradata) | |||
| return AVERROR(ENOMEM); | |||
| // Audio | |||
| audio_dec = audio_stream->codec; | |||
| audio_dec->codec_type = CODEC_TYPE_AUDIO; | |||
| audio_dec->codec_id = CODEC_ID_ADPCM_IMA_WS; | |||
| audio_dec->channels = 1; | |||
| audio_dec->sample_rate = 22050; | |||
| // Video | |||
| video_dec = video_stream->codec; | |||
| video_dec->codec_type = CODEC_TYPE_VIDEO; | |||
| video_dec->codec_id = CODEC_ID_YOP; | |||
| url_fskip(pb, 6); | |||
| frame_rate = get_byte(pb); | |||
| yop->frame_size = get_byte(pb) * 2048; | |||
| video_dec->width = get_le16(pb); | |||
| video_dec->height = get_le16(pb); | |||
| video_stream->sample_aspect_ratio = (AVRational){1, 2}; | |||
| ret = get_buffer(pb, video_dec->extradata, 8); | |||
| if (ret < 8) | |||
| return ret < 0 ? ret : AVERROR_EOF; | |||
| yop->palette_size = video_dec->extradata[0] * 3 + 4; | |||
| yop->audio_block_length = AV_RL16(video_dec->extradata + 6); | |||
| // 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920 | |||
| if (yop->audio_block_length < 920 || | |||
| yop->audio_block_length + yop->palette_size >= yop->frame_size) { | |||
| av_log(s, AV_LOG_ERROR, "YOP has invalid header\n"); | |||
| return AVERROR_INVALIDDATA; | |||
| } | |||
| url_fseek(pb, 2048, SEEK_SET); | |||
| av_set_pts_info(video_stream, 32, 1, frame_rate); | |||
| return 0; | |||
| } | |||
| static int yop_read_packet(AVFormatContext *s, AVPacket *pkt) | |||
| { | |||
| YopDecContext *yop = s->priv_data; | |||
| ByteIOContext *pb = s->pb; | |||
| int ret; | |||
| int actual_video_data_size = yop->frame_size - | |||
| yop->audio_block_length - yop->palette_size; | |||
| yop->video_packet.stream_index = 1; | |||
| if (yop->video_packet.data) { | |||
| *pkt = yop->video_packet; | |||
| yop->video_packet.data = NULL; | |||
| yop->video_packet.size = 0; | |||
| pkt->data[0] = yop->odd_frame; | |||
| pkt->flags |= PKT_FLAG_KEY; | |||
| yop->odd_frame ^= 1; | |||
| return pkt->size; | |||
| } | |||
| ret = av_new_packet(&yop->video_packet, | |||
| yop->frame_size - yop->audio_block_length); | |||
| if (ret < 0) | |||
| return ret; | |||
| yop->video_packet.pos = url_ftell(pb); | |||
| ret = get_buffer(pb, yop->video_packet.data, yop->palette_size); | |||
| if (ret < 0) { | |||
| goto err_out; | |||
| }else if (ret < yop->palette_size) { | |||
| ret = AVERROR_EOF; | |||
| goto err_out; | |||
| } | |||
| ret = av_get_packet(pb, pkt, 920); | |||
| if (ret < 0) | |||
| goto err_out; | |||
| // Set position to the start of the frame | |||
| pkt->pos = yop->video_packet.pos; | |||
| url_fskip(pb, yop->audio_block_length - ret); | |||
| ret = get_buffer(pb, yop->video_packet.data + yop->palette_size, | |||
| actual_video_data_size); | |||
| if (ret < 0) | |||
| goto err_out; | |||
| else if (ret < actual_video_data_size) | |||
| av_shrink_packet(&yop->video_packet, yop->palette_size + ret); | |||
| // Arbitrarily return the audio data first | |||
| return yop->audio_block_length; | |||
| err_out: | |||
| av_free_packet(&yop->video_packet); | |||
| return ret; | |||
| } | |||
| static int yop_read_close(AVFormatContext *s) | |||
| { | |||
| YopDecContext *yop = s->priv_data; | |||
| av_free_packet(&yop->video_packet); | |||
| return 0; | |||
| } | |||
| static int yop_read_seek(AVFormatContext *s, int stream_index, | |||
| int64_t timestamp, int flags) | |||
| { | |||
| YopDecContext *yop = s->priv_data; | |||
| int64_t frame_pos, pos_min, pos_max; | |||
| int frame_count; | |||
| av_free_packet(&yop->video_packet); | |||
| if (!stream_index) | |||
| return -1; | |||
| pos_min = s->data_offset; | |||
| pos_max = url_fsize(s->pb) - yop->frame_size; | |||
| frame_count = (pos_max - pos_min) / yop->frame_size; | |||
| timestamp = FFMAX(0, FFMIN(frame_count, timestamp)); | |||
| frame_pos = timestamp * yop->frame_size + pos_min; | |||
| yop->odd_frame = timestamp & 1; | |||
| url_fseek(s->pb, frame_pos, SEEK_SET); | |||
| return 0; | |||
| } | |||
| AVInputFormat yop_demuxer = { | |||
| "yop", | |||
| NULL_IF_CONFIG_SMALL("Psygnosis YOP Format"), | |||
| sizeof(YopDecContext), | |||
| yop_probe, | |||
| yop_read_header, | |||
| yop_read_packet, | |||
| yop_read_close, | |||
| yop_read_seek, | |||
| .extensions = "yop", | |||
| .flags = AVFMT_GENERIC_INDEX, | |||
| }; | |||