2 * Psygnosis YOP demuxer
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>
8 * This file is part of FFmpeg.
10 * FFmpeg 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.
15 * FFmpeg 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.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with FFmpeg; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "libavutil/channel_layout.h"
26 #include "libavutil/intreadwrite.h"
30 typedef struct yop_dec_context
{
31 AVPacket video_packet
;
35 int audio_block_length
;
39 static int yop_probe(AVProbeData
*probe_packet
)
41 if (AV_RB16(probe_packet
->buf
) == AV_RB16("YO") &&
42 probe_packet
->buf
[2]<10 &&
43 probe_packet
->buf
[3]<10 &&
44 probe_packet
->buf
[6] &&
45 probe_packet
->buf
[7] &&
46 !(probe_packet
->buf
[8] & 1) &&
47 !(probe_packet
->buf
[10] & 1) &&
48 AV_RL16(probe_packet
->buf
+ 12 + 6) >= 920 &&
49 AV_RL16(probe_packet
->buf
+ 12 + 6) < probe_packet
->buf
[12] * 3 + 4 + probe_packet
->buf
[7] * 2048
51 return AVPROBE_SCORE_MAX
* 3 / 4;
56 static int yop_read_header(AVFormatContext
*s
)
58 YopDecContext
*yop
= s
->priv_data
;
59 AVIOContext
*pb
= s
->pb
;
61 AVCodecContext
*audio_dec
, *video_dec
;
62 AVStream
*audio_stream
, *video_stream
;
66 audio_stream
= avformat_new_stream(s
, NULL
);
67 video_stream
= avformat_new_stream(s
, NULL
);
68 if (!audio_stream
|| !video_stream
)
69 return AVERROR(ENOMEM
);
71 // Extra data that will be passed to the decoder
72 if (ff_alloc_extradata(video_stream
->codec
, 8))
73 return AVERROR(ENOMEM
);
76 audio_dec
= audio_stream
->codec
;
77 audio_dec
->codec_type
= AVMEDIA_TYPE_AUDIO
;
78 audio_dec
->codec_id
= AV_CODEC_ID_ADPCM_IMA_APC
;
79 audio_dec
->channels
= 1;
80 audio_dec
->channel_layout
= AV_CH_LAYOUT_MONO
;
81 audio_dec
->sample_rate
= 22050;
84 video_dec
= video_stream
->codec
;
85 video_dec
->codec_type
= AVMEDIA_TYPE_VIDEO
;
86 video_dec
->codec_id
= AV_CODEC_ID_YOP
;
90 frame_rate
= avio_r8(pb
);
91 yop
->frame_size
= avio_r8(pb
) * 2048;
92 video_dec
->width
= avio_rl16(pb
);
93 video_dec
->height
= avio_rl16(pb
);
95 video_stream
->sample_aspect_ratio
= (AVRational
){1, 2};
97 ret
= avio_read(pb
, video_dec
->extradata
, 8);
99 return ret
< 0 ? ret
: AVERROR_EOF
;
101 yop
->palette_size
= video_dec
->extradata
[0] * 3 + 4;
102 yop
->audio_block_length
= AV_RL16(video_dec
->extradata
+ 6);
104 video_dec
->bit_rate
= 8 * (yop
->frame_size
- yop
->audio_block_length
) * frame_rate
;
106 // 1840 samples per frame, 1 nibble per sample; hence 1840/2 = 920
107 if (yop
->audio_block_length
< 920 ||
108 yop
->audio_block_length
+ yop
->palette_size
>= yop
->frame_size
) {
109 av_log(s
, AV_LOG_ERROR
, "YOP has invalid header\n");
110 return AVERROR_INVALIDDATA
;
113 avio_seek(pb
, 2048, SEEK_SET
);
115 avpriv_set_pts_info(video_stream
, 32, 1, frame_rate
);
120 static int yop_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
122 YopDecContext
*yop
= s
->priv_data
;
123 AVIOContext
*pb
= s
->pb
;
126 int actual_video_data_size
= yop
->frame_size
-
127 yop
->audio_block_length
- yop
->palette_size
;
129 yop
->video_packet
.stream_index
= 1;
131 if (yop
->video_packet
.data
) {
132 *pkt
= yop
->video_packet
;
133 yop
->video_packet
.data
= NULL
;
134 yop
->video_packet
.buf
= NULL
;
135 #if FF_API_DESTRUCT_PACKET
136 FF_DISABLE_DEPRECATION_WARNINGS
137 yop
->video_packet
.destruct
= NULL
;
138 FF_ENABLE_DEPRECATION_WARNINGS
140 yop
->video_packet
.size
= 0;
141 pkt
->data
[0] = yop
->odd_frame
;
142 pkt
->flags
|= AV_PKT_FLAG_KEY
;
146 ret
= av_new_packet(&yop
->video_packet
,
147 yop
->frame_size
- yop
->audio_block_length
);
151 yop
->video_packet
.pos
= avio_tell(pb
);
153 ret
= avio_read(pb
, yop
->video_packet
.data
, yop
->palette_size
);
156 }else if (ret
< yop
->palette_size
) {
161 ret
= av_get_packet(pb
, pkt
, 920);
165 // Set position to the start of the frame
166 pkt
->pos
= yop
->video_packet
.pos
;
168 avio_skip(pb
, yop
->audio_block_length
- ret
);
170 ret
= avio_read(pb
, yop
->video_packet
.data
+ yop
->palette_size
,
171 actual_video_data_size
);
174 else if (ret
< actual_video_data_size
)
175 av_shrink_packet(&yop
->video_packet
, yop
->palette_size
+ ret
);
177 // Arbitrarily return the audio data first
178 return yop
->audio_block_length
;
181 av_free_packet(&yop
->video_packet
);
185 static int yop_read_close(AVFormatContext
*s
)
187 YopDecContext
*yop
= s
->priv_data
;
188 av_free_packet(&yop
->video_packet
);
192 static int yop_read_seek(AVFormatContext
*s
, int stream_index
,
193 int64_t timestamp
, int flags
)
195 YopDecContext
*yop
= s
->priv_data
;
196 int64_t frame_pos
, pos_min
, pos_max
;
202 pos_min
= s
->data_offset
;
203 pos_max
= avio_size(s
->pb
) - yop
->frame_size
;
204 frame_count
= (pos_max
- pos_min
) / yop
->frame_size
;
206 timestamp
= FFMAX(0, FFMIN(frame_count
, timestamp
));
208 frame_pos
= timestamp
* yop
->frame_size
+ pos_min
;
210 if (avio_seek(s
->pb
, frame_pos
, SEEK_SET
) < 0)
213 av_free_packet(&yop
->video_packet
);
214 yop
->odd_frame
= timestamp
& 1;
219 AVInputFormat ff_yop_demuxer
= {
221 .long_name
= NULL_IF_CONFIG_SMALL("Psygnosis YOP"),
222 .priv_data_size
= sizeof(YopDecContext
),
223 .read_probe
= yop_probe
,
224 .read_header
= yop_read_header
,
225 .read_packet
= yop_read_packet
,
226 .read_close
= yop_read_close
,
227 .read_seek
= yop_read_seek
,
229 .flags
= AVFMT_GENERIC_INDEX
,