blob: 462b8238606429fa3f8606df1397f5ac9f44c913
1 | /* |
2 | * YUV4MPEG demuxer |
3 | * Copyright (c) 2001, 2002, 2003 Fabrice Bellard |
4 | * |
5 | * This file is part of FFmpeg. |
6 | * |
7 | * FFmpeg is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * |
12 | * FFmpeg is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with FFmpeg; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | #include "libavutil/imgutils.h" |
23 | |
24 | #include "avformat.h" |
25 | #include "internal.h" |
26 | #include "yuv4mpeg.h" |
27 | |
28 | /* Header size increased to allow room for optional flags */ |
29 | #define MAX_YUV4_HEADER 80 |
30 | #define MAX_FRAME_HEADER 80 |
31 | |
32 | static int yuv4_read_header(AVFormatContext *s) |
33 | { |
34 | char header[MAX_YUV4_HEADER + 10]; // Include headroom for |
35 | // the longest option |
36 | char *tokstart, *tokend, *header_end; |
37 | int i; |
38 | AVIOContext *pb = s->pb; |
39 | int width = -1, height = -1, raten = 0, |
40 | rated = 0, aspectn = 0, aspectd = 0; |
41 | enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE, alt_pix_fmt = AV_PIX_FMT_NONE; |
42 | enum AVChromaLocation chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED; |
43 | enum AVFieldOrder field_order = AV_FIELD_UNKNOWN; |
44 | AVStream *st; |
45 | |
46 | for (i = 0; i < MAX_YUV4_HEADER; i++) { |
47 | header[i] = avio_r8(pb); |
48 | if (header[i] == '\n') { |
49 | header[i + 1] = 0x20; // Add a space after last option. |
50 | // Makes parsing "444" vs "444alpha" easier. |
51 | header[i + 2] = 0; |
52 | break; |
53 | } |
54 | } |
55 | if (i == MAX_YUV4_HEADER) |
56 | return -1; |
57 | if (strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC))) |
58 | return -1; |
59 | |
60 | header_end = &header[i + 1]; // Include space |
61 | for (tokstart = &header[strlen(Y4M_MAGIC) + 1]; |
62 | tokstart < header_end; tokstart++) { |
63 | if (*tokstart == 0x20) |
64 | continue; |
65 | switch (*tokstart++) { |
66 | case 'W': // Width. Required. |
67 | width = strtol(tokstart, &tokend, 10); |
68 | tokstart = tokend; |
69 | break; |
70 | case 'H': // Height. Required. |
71 | height = strtol(tokstart, &tokend, 10); |
72 | tokstart = tokend; |
73 | break; |
74 | case 'C': // Color space |
75 | if (strncmp("420jpeg", tokstart, 7) == 0) { |
76 | pix_fmt = AV_PIX_FMT_YUV420P; |
77 | chroma_sample_location = AVCHROMA_LOC_CENTER; |
78 | } else if (strncmp("420mpeg2", tokstart, 8) == 0) { |
79 | pix_fmt = AV_PIX_FMT_YUV420P; |
80 | chroma_sample_location = AVCHROMA_LOC_LEFT; |
81 | } else if (strncmp("420paldv", tokstart, 8) == 0) { |
82 | pix_fmt = AV_PIX_FMT_YUV420P; |
83 | chroma_sample_location = AVCHROMA_LOC_TOPLEFT; |
84 | } else if (strncmp("420p16", tokstart, 6) == 0) { |
85 | pix_fmt = AV_PIX_FMT_YUV420P16; |
86 | } else if (strncmp("422p16", tokstart, 6) == 0) { |
87 | pix_fmt = AV_PIX_FMT_YUV422P16; |
88 | } else if (strncmp("444p16", tokstart, 6) == 0) { |
89 | pix_fmt = AV_PIX_FMT_YUV444P16; |
90 | } else if (strncmp("420p14", tokstart, 6) == 0) { |
91 | pix_fmt = AV_PIX_FMT_YUV420P14; |
92 | } else if (strncmp("422p14", tokstart, 6) == 0) { |
93 | pix_fmt = AV_PIX_FMT_YUV422P14; |
94 | } else if (strncmp("444p14", tokstart, 6) == 0) { |
95 | pix_fmt = AV_PIX_FMT_YUV444P14; |
96 | } else if (strncmp("420p12", tokstart, 6) == 0) { |
97 | pix_fmt = AV_PIX_FMT_YUV420P12; |
98 | } else if (strncmp("422p12", tokstart, 6) == 0) { |
99 | pix_fmt = AV_PIX_FMT_YUV422P12; |
100 | } else if (strncmp("444p12", tokstart, 6) == 0) { |
101 | pix_fmt = AV_PIX_FMT_YUV444P12; |
102 | } else if (strncmp("420p10", tokstart, 6) == 0) { |
103 | pix_fmt = AV_PIX_FMT_YUV420P10; |
104 | } else if (strncmp("422p10", tokstart, 6) == 0) { |
105 | pix_fmt = AV_PIX_FMT_YUV422P10; |
106 | } else if (strncmp("444p10", tokstart, 6) == 0) { |
107 | pix_fmt = AV_PIX_FMT_YUV444P10; |
108 | } else if (strncmp("420p9", tokstart, 5) == 0) { |
109 | pix_fmt = AV_PIX_FMT_YUV420P9; |
110 | } else if (strncmp("422p9", tokstart, 5) == 0) { |
111 | pix_fmt = AV_PIX_FMT_YUV422P9; |
112 | } else if (strncmp("444p9", tokstart, 5) == 0) { |
113 | pix_fmt = AV_PIX_FMT_YUV444P9; |
114 | } else if (strncmp("420", tokstart, 3) == 0) { |
115 | pix_fmt = AV_PIX_FMT_YUV420P; |
116 | chroma_sample_location = AVCHROMA_LOC_CENTER; |
117 | } else if (strncmp("411", tokstart, 3) == 0) { |
118 | pix_fmt = AV_PIX_FMT_YUV411P; |
119 | } else if (strncmp("422", tokstart, 3) == 0) { |
120 | pix_fmt = AV_PIX_FMT_YUV422P; |
121 | } else if (strncmp("444alpha", tokstart, 8) == 0 ) { |
122 | av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 " |
123 | "YUV4MPEG stream.\n"); |
124 | return -1; |
125 | } else if (strncmp("444", tokstart, 3) == 0) { |
126 | pix_fmt = AV_PIX_FMT_YUV444P; |
127 | } else if (strncmp("mono16", tokstart, 6) == 0) { |
128 | pix_fmt = AV_PIX_FMT_GRAY16; |
129 | } else if (strncmp("mono", tokstart, 4) == 0) { |
130 | pix_fmt = AV_PIX_FMT_GRAY8; |
131 | } else { |
132 | av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown " |
133 | "pixel format.\n"); |
134 | return -1; |
135 | } |
136 | while (tokstart < header_end && *tokstart != 0x20) |
137 | tokstart++; |
138 | break; |
139 | case 'I': // Interlace type |
140 | switch (*tokstart++){ |
141 | case '?': |
142 | field_order = AV_FIELD_UNKNOWN; |
143 | break; |
144 | case 'p': |
145 | field_order = AV_FIELD_PROGRESSIVE; |
146 | break; |
147 | case 't': |
148 | field_order = AV_FIELD_TT; |
149 | break; |
150 | case 'b': |
151 | field_order = AV_FIELD_BB; |
152 | break; |
153 | case 'm': |
154 | av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed " |
155 | "interlaced and non-interlaced frames.\n"); |
156 | default: |
157 | av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n"); |
158 | return AVERROR(EINVAL); |
159 | } |
160 | break; |
161 | case 'F': // Frame rate |
162 | sscanf(tokstart, "%d:%d", &raten, &rated); // 0:0 if unknown |
163 | while (tokstart < header_end && *tokstart != 0x20) |
164 | tokstart++; |
165 | break; |
166 | case 'A': // Pixel aspect |
167 | sscanf(tokstart, "%d:%d", &aspectn, &aspectd); // 0:0 if unknown |
168 | while (tokstart < header_end && *tokstart != 0x20) |
169 | tokstart++; |
170 | break; |
171 | case 'X': // Vendor extensions |
172 | if (strncmp("YSCSS=", tokstart, 6) == 0) { |
173 | // Older nonstandard pixel format representation |
174 | tokstart += 6; |
175 | if (strncmp("420JPEG", tokstart, 7) == 0) |
176 | alt_pix_fmt = AV_PIX_FMT_YUV420P; |
177 | else if (strncmp("420MPEG2", tokstart, 8) == 0) |
178 | alt_pix_fmt = AV_PIX_FMT_YUV420P; |
179 | else if (strncmp("420PALDV", tokstart, 8) == 0) |
180 | alt_pix_fmt = AV_PIX_FMT_YUV420P; |
181 | else if (strncmp("420P9", tokstart, 5) == 0) |
182 | alt_pix_fmt = AV_PIX_FMT_YUV420P9; |
183 | else if (strncmp("422P9", tokstart, 5) == 0) |
184 | alt_pix_fmt = AV_PIX_FMT_YUV422P9; |
185 | else if (strncmp("444P9", tokstart, 5) == 0) |
186 | alt_pix_fmt = AV_PIX_FMT_YUV444P9; |
187 | else if (strncmp("420P10", tokstart, 6) == 0) |
188 | alt_pix_fmt = AV_PIX_FMT_YUV420P10; |
189 | else if (strncmp("422P10", tokstart, 6) == 0) |
190 | alt_pix_fmt = AV_PIX_FMT_YUV422P10; |
191 | else if (strncmp("444P10", tokstart, 6) == 0) |
192 | alt_pix_fmt = AV_PIX_FMT_YUV444P10; |
193 | else if (strncmp("420P12", tokstart, 6) == 0) |
194 | alt_pix_fmt = AV_PIX_FMT_YUV420P12; |
195 | else if (strncmp("422P12", tokstart, 6) == 0) |
196 | alt_pix_fmt = AV_PIX_FMT_YUV422P12; |
197 | else if (strncmp("444P12", tokstart, 6) == 0) |
198 | alt_pix_fmt = AV_PIX_FMT_YUV444P12; |
199 | else if (strncmp("420P14", tokstart, 6) == 0) |
200 | alt_pix_fmt = AV_PIX_FMT_YUV420P14; |
201 | else if (strncmp("422P14", tokstart, 6) == 0) |
202 | alt_pix_fmt = AV_PIX_FMT_YUV422P14; |
203 | else if (strncmp("444P14", tokstart, 6) == 0) |
204 | alt_pix_fmt = AV_PIX_FMT_YUV444P14; |
205 | else if (strncmp("420P16", tokstart, 6) == 0) |
206 | alt_pix_fmt = AV_PIX_FMT_YUV420P16; |
207 | else if (strncmp("422P16", tokstart, 6) == 0) |
208 | alt_pix_fmt = AV_PIX_FMT_YUV422P16; |
209 | else if (strncmp("444P16", tokstart, 6) == 0) |
210 | alt_pix_fmt = AV_PIX_FMT_YUV444P16; |
211 | else if (strncmp("411", tokstart, 3) == 0) |
212 | alt_pix_fmt = AV_PIX_FMT_YUV411P; |
213 | else if (strncmp("422", tokstart, 3) == 0) |
214 | alt_pix_fmt = AV_PIX_FMT_YUV422P; |
215 | else if (strncmp("444", tokstart, 3) == 0) |
216 | alt_pix_fmt = AV_PIX_FMT_YUV444P; |
217 | } |
218 | while (tokstart < header_end && *tokstart != 0x20) |
219 | tokstart++; |
220 | break; |
221 | } |
222 | } |
223 | |
224 | if (width == -1 || height == -1) { |
225 | av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n"); |
226 | return -1; |
227 | } |
228 | |
229 | if (pix_fmt == AV_PIX_FMT_NONE) { |
230 | if (alt_pix_fmt == AV_PIX_FMT_NONE) |
231 | pix_fmt = AV_PIX_FMT_YUV420P; |
232 | else |
233 | pix_fmt = alt_pix_fmt; |
234 | } |
235 | |
236 | if (raten <= 0 || rated <= 0) { |
237 | // Frame rate unknown |
238 | raten = 25; |
239 | rated = 1; |
240 | } |
241 | |
242 | if (aspectn == 0 && aspectd == 0) { |
243 | // Pixel aspect unknown |
244 | aspectd = 1; |
245 | } |
246 | |
247 | st = avformat_new_stream(s, NULL); |
248 | if (!st) |
249 | return AVERROR(ENOMEM); |
250 | st->codecpar->width = width; |
251 | st->codecpar->height = height; |
252 | av_reduce(&raten, &rated, raten, rated, (1UL << 31) - 1); |
253 | avpriv_set_pts_info(st, 64, rated, raten); |
254 | st->avg_frame_rate = av_inv_q(st->time_base); |
255 | st->codecpar->format = pix_fmt; |
256 | st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; |
257 | st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO; |
258 | st->sample_aspect_ratio = (AVRational){ aspectn, aspectd }; |
259 | st->codecpar->chroma_location = chroma_sample_location; |
260 | st->codecpar->field_order = field_order; |
261 | s->packet_size = av_image_get_buffer_size(st->codecpar->format, width, height, 1) + Y4M_FRAME_MAGIC_LEN; |
262 | if ((int) s->packet_size < 0) |
263 | return s->packet_size; |
264 | s->internal->data_offset = avio_tell(pb); |
265 | |
266 | st->duration = (avio_size(pb) - avio_tell(pb)) / s->packet_size; |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt) |
272 | { |
273 | int i; |
274 | char header[MAX_FRAME_HEADER+1]; |
275 | int ret; |
276 | int64_t off = avio_tell(s->pb); |
277 | |
278 | for (i = 0; i < MAX_FRAME_HEADER; i++) { |
279 | header[i] = avio_r8(s->pb); |
280 | if (header[i] == '\n') { |
281 | header[i + 1] = 0; |
282 | break; |
283 | } |
284 | } |
285 | if (s->pb->error) |
286 | return s->pb->error; |
287 | else if (s->pb->eof_reached) |
288 | return AVERROR_EOF; |
289 | else if (i == MAX_FRAME_HEADER) |
290 | return AVERROR_INVALIDDATA; |
291 | |
292 | if (strncmp(header, Y4M_FRAME_MAGIC, strlen(Y4M_FRAME_MAGIC))) |
293 | return AVERROR_INVALIDDATA; |
294 | |
295 | ret = av_get_packet(s->pb, pkt, s->packet_size - Y4M_FRAME_MAGIC_LEN); |
296 | if (ret < 0) |
297 | return ret; |
298 | else if (ret != s->packet_size - Y4M_FRAME_MAGIC_LEN) { |
299 | av_packet_unref(pkt); |
300 | return s->pb->eof_reached ? AVERROR_EOF : AVERROR(EIO); |
301 | } |
302 | pkt->stream_index = 0; |
303 | pkt->pts = (off - s->internal->data_offset) / s->packet_size; |
304 | pkt->duration = 1; |
305 | return 0; |
306 | } |
307 | |
308 | static int yuv4_read_seek(AVFormatContext *s, int stream_index, |
309 | int64_t pts, int flags) |
310 | { |
311 | if (avio_seek(s->pb, pts * s->packet_size + s->internal->data_offset, SEEK_SET) < 0) |
312 | return -1; |
313 | return 0; |
314 | } |
315 | |
316 | static int yuv4_probe(AVProbeData *pd) |
317 | { |
318 | /* check file header */ |
319 | if (strncmp(pd->buf, Y4M_MAGIC, sizeof(Y4M_MAGIC) - 1) == 0) |
320 | return AVPROBE_SCORE_MAX; |
321 | else |
322 | return 0; |
323 | } |
324 | |
325 | AVInputFormat ff_yuv4mpegpipe_demuxer = { |
326 | .name = "yuv4mpegpipe", |
327 | .long_name = NULL_IF_CONFIG_SMALL("YUV4MPEG pipe"), |
328 | .read_probe = yuv4_probe, |
329 | .read_header = yuv4_read_header, |
330 | .read_packet = yuv4_read_packet, |
331 | .read_seek = yuv4_read_seek, |
332 | .extensions = "y4m", |
333 | }; |
334 |