ffmpeg / libavformat / yop.c @ 9dd94f83
History | View | Annotate | Download (6.18 KB)
1 |
/**
|
---|---|
2 |
* @file
|
3 |
* Psygnosis YOP demuxer
|
4 |
*
|
5 |
* Copyright (C) 2010 Mohamed Naufal Basheer <naufal11@gmail.com>
|
6 |
* derived from the code by
|
7 |
* Copyright (C) 2009 Thomas P. Higdon <thomas.p.higdon@gmail.com>
|
8 |
*
|
9 |
* This file is part of Libav.
|
10 |
*
|
11 |
* Libav is free software; you can redistribute it and/or
|
12 |
* modify it under the terms of the GNU Lesser General Public
|
13 |
* License as published by the Free Software Foundation; either
|
14 |
* version 2.1 of the License, or (at your option) any later version.
|
15 |
*
|
16 |
* Libav is distributed in the hope that it will be useful,
|
17 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
19 |
* Lesser General Public License for more details.
|
20 |
*
|
21 |
* You should have received a copy of the GNU Lesser General Public
|
22 |
* License along with Libav; if not, write to the Free Software
|
23 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
24 |
*/
|
25 |
|
26 |
#include "libavutil/intreadwrite.h" |
27 |
#include "avformat.h" |
28 |
|
29 |
typedef 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 |
|
38 |
static 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 |
|
50 |
static 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 = av_new_stream(s, 0);
|
61 |
video_stream = av_new_stream(s, 1);
|
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 |
av_set_pts_info(video_stream, 32, 1, frame_rate); |
110 |
|
111 |
return 0; |
112 |
} |
113 |
|
114 |
static 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 |
|
168 |
err_out:
|
169 |
av_free_packet(&yop->video_packet); |
170 |
return ret;
|
171 |
} |
172 |
|
173 |
static 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 |
|
180 |
static 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 |
|
205 |
AVInputFormat ff_yop_demuxer = { |
206 |
"yop",
|
207 |
NULL_IF_CONFIG_SMALL("Psygnosis YOP Format"),
|
208 |
sizeof(YopDecContext),
|
209 |
yop_probe, |
210 |
yop_read_header, |
211 |
yop_read_packet, |
212 |
yop_read_close, |
213 |
yop_read_seek, |
214 |
.extensions = "yop",
|
215 |
.flags = AVFMT_GENERIC_INDEX, |
216 |
}; |