ffmpeg / libavformat / yop.c @ 9dd94f83
History | View | Annotate | Download (6.18 KB)
1 | 148bcae9 | Mohamed Naufal | /**
|
---|---|---|---|
2 | ba87f080 | Diego Biurrun | * @file
|
3 | 148bcae9 | Mohamed Naufal | * 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 | 2912e87a | Mans Rullgard | * This file is part of Libav.
|
10 | 148bcae9 | Mohamed Naufal | *
|
11 | 2912e87a | Mans Rullgard | * Libav is free software; you can redistribute it and/or
|
12 | 148bcae9 | Mohamed Naufal | * 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 | 2912e87a | Mans Rullgard | * Libav is distributed in the hope that it will be useful,
|
17 | 148bcae9 | Mohamed Naufal | * 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 | 2912e87a | Mans Rullgard | * License along with Libav; if not, write to the Free Software
|
23 | 148bcae9 | Mohamed Naufal | * 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 | ae628ec1 | Anton Khirnov | AVIOContext *pb = s->pb; |
54 | 148bcae9 | Mohamed Naufal | |
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 | 72415b2a | Stefano Sabatini | audio_dec->codec_type = AVMEDIA_TYPE_AUDIO; |
75 | 148bcae9 | Mohamed Naufal | 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 | 72415b2a | Stefano Sabatini | video_dec->codec_type = AVMEDIA_TYPE_VIDEO; |
82 | 148bcae9 | Mohamed Naufal | video_dec->codec_id = CODEC_ID_YOP; |
83 | |||
84 | 45a8a02a | Anton Khirnov | avio_skip(pb, 6);
|
85 | 148bcae9 | Mohamed Naufal | |
86 | b7effd4e | Anton Khirnov | 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 | 148bcae9 | Mohamed Naufal | |
91 | video_stream->sample_aspect_ratio = (AVRational){1, 2}; |
||
92 | |||
93 | b7effd4e | Anton Khirnov | ret = avio_read(pb, video_dec->extradata, 8);
|
94 | 148bcae9 | Mohamed Naufal | 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 | 6b4aa5da | Anton Khirnov | avio_seek(pb, 2048, SEEK_SET);
|
108 | 148bcae9 | Mohamed Naufal | |
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 | ae628ec1 | Anton Khirnov | AVIOContext *pb = s->pb; |
118 | 148bcae9 | Mohamed Naufal | |
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 | cc947f04 | Jean-Daniel Dupas | pkt->flags |= AV_PKT_FLAG_KEY; |
131 | 148bcae9 | Mohamed Naufal | 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 | a2704c97 | Anton Khirnov | yop->video_packet.pos = avio_tell(pb); |
140 | 148bcae9 | Mohamed Naufal | |
141 | b7effd4e | Anton Khirnov | ret = avio_read(pb, yop->video_packet.data, yop->palette_size); |
142 | 148bcae9 | Mohamed Naufal | 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 | 45a8a02a | Anton Khirnov | avio_skip(pb, yop->audio_block_length - ret); |
157 | 148bcae9 | Mohamed Naufal | |
158 | b7effd4e | Anton Khirnov | ret = avio_read(pb, yop->video_packet.data + yop->palette_size, |
159 | 148bcae9 | Mohamed Naufal | 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 | 76aa876e | Anton Khirnov | pos_max = avio_size(s->pb) - yop->frame_size; |
194 | 148bcae9 | Mohamed Naufal | 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 | 6b4aa5da | Anton Khirnov | avio_seek(s->pb, frame_pos, SEEK_SET); |
202 | 148bcae9 | Mohamed Naufal | return 0; |
203 | } |
||
204 | |||
205 | c6610a21 | Diego Elio Pettenò | AVInputFormat ff_yop_demuxer = { |
206 | 148bcae9 | Mohamed Naufal | "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 | }; |