3 * Copyright (c) 2001, 2002, 2003 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/avstring.h"
23 #include "libavutil/imgutils.h"
30 /* Header size increased to allow room for optional flags */
31 #define MAX_YUV4_HEADER 128
32 #define MAX_FRAME_HEADER 80
34 static int yuv4_read_header(AVFormatContext
*s
)
36 char header
[MAX_YUV4_HEADER
+ 10]; // Include headroom for
38 char *tokstart
, *tokend
, *header_end
;
40 AVIOContext
*pb
= s
->pb
;
41 int width
= -1, height
= -1, raten
= 0,
42 rated
= 0, aspectn
= 0, aspectd
= 0;
43 enum AVPixelFormat pix_fmt
= AV_PIX_FMT_NONE
, alt_pix_fmt
= AV_PIX_FMT_NONE
;
44 enum AVChromaLocation chroma_sample_location
= AVCHROMA_LOC_UNSPECIFIED
;
45 enum AVFieldOrder field_order
= AV_FIELD_UNKNOWN
;
46 enum AVColorRange color_range
= AVCOL_RANGE_UNSPECIFIED
;
50 for (i
= 0; i
< MAX_YUV4_HEADER
; i
++) {
51 header
[i
] = avio_r8(pb
);
52 if (header
[i
] == '\n') {
53 header
[i
+ 1] = 0x20; // Add a space after last option.
54 // Makes parsing "444" vs "444alpha" easier.
59 if (i
== MAX_YUV4_HEADER
) {
60 av_log(s
, AV_LOG_ERROR
, "Header too large.\n");
61 return AVERROR(EINVAL
);
63 if (strncmp(header
, Y4M_MAGIC
, strlen(Y4M_MAGIC
))) {
64 av_log(s
, AV_LOG_ERROR
, "Invalid magic number for yuv4mpeg.\n");
65 return AVERROR(EINVAL
);
68 header_end
= &header
[i
+ 1]; // Include space
69 for (tokstart
= &header
[strlen(Y4M_MAGIC
) + 1];
70 tokstart
< header_end
; tokstart
++) {
71 if (*tokstart
== 0x20)
73 switch (*tokstart
++) {
74 case 'W': // Width. Required.
75 width
= strtol(tokstart
, &tokend
, 10);
78 case 'H': // Height. Required.
79 height
= strtol(tokstart
, &tokend
, 10);
82 case 'C': // Color space
85 #define MAX_PIX_FMT_LENGTH 8
86 char name
[MAX_PIX_FMT_LENGTH
+ 1];
87 #undef MAX_PIX_FMT_LENGTH
88 enum AVPixelFormat pix_fmt
;
89 enum AVChromaLocation chroma_loc
;
91 { "420jpeg", AV_PIX_FMT_YUV420P
, AVCHROMA_LOC_CENTER
},
92 { "420mpeg2", AV_PIX_FMT_YUV420P
, AVCHROMA_LOC_LEFT
},
93 { "420paldv", AV_PIX_FMT_YUV420P
, AVCHROMA_LOC_TOPLEFT
},
94 { "420p16", AV_PIX_FMT_YUV420P16
, AVCHROMA_LOC_UNSPECIFIED
},
95 { "422p16", AV_PIX_FMT_YUV422P16
, AVCHROMA_LOC_UNSPECIFIED
},
96 { "444p16", AV_PIX_FMT_YUV444P16
, AVCHROMA_LOC_UNSPECIFIED
},
97 { "420p14", AV_PIX_FMT_YUV420P14
, AVCHROMA_LOC_UNSPECIFIED
},
98 { "422p14", AV_PIX_FMT_YUV422P14
, AVCHROMA_LOC_UNSPECIFIED
},
99 { "444p14", AV_PIX_FMT_YUV444P14
, AVCHROMA_LOC_UNSPECIFIED
},
100 { "420p12", AV_PIX_FMT_YUV420P12
, AVCHROMA_LOC_UNSPECIFIED
},
101 { "422p12", AV_PIX_FMT_YUV422P12
, AVCHROMA_LOC_UNSPECIFIED
},
102 { "444p12", AV_PIX_FMT_YUV444P12
, AVCHROMA_LOC_UNSPECIFIED
},
103 { "420p10", AV_PIX_FMT_YUV420P10
, AVCHROMA_LOC_UNSPECIFIED
},
104 { "422p10", AV_PIX_FMT_YUV422P10
, AVCHROMA_LOC_UNSPECIFIED
},
105 { "444p10", AV_PIX_FMT_YUV444P10
, AVCHROMA_LOC_UNSPECIFIED
},
106 { "420p9", AV_PIX_FMT_YUV420P9
, AVCHROMA_LOC_UNSPECIFIED
},
107 { "422p9", AV_PIX_FMT_YUV422P9
, AVCHROMA_LOC_UNSPECIFIED
},
108 { "444p9", AV_PIX_FMT_YUV444P9
, AVCHROMA_LOC_UNSPECIFIED
},
109 { "420", AV_PIX_FMT_YUV420P
, AVCHROMA_LOC_CENTER
},
110 { "411", AV_PIX_FMT_YUV411P
, AVCHROMA_LOC_UNSPECIFIED
},
111 { "422", AV_PIX_FMT_YUV422P
, AVCHROMA_LOC_UNSPECIFIED
},
112 { "444alpha", AV_PIX_FMT_YUVA444P
, AVCHROMA_LOC_UNSPECIFIED
},
113 { "444", AV_PIX_FMT_YUV444P
, AVCHROMA_LOC_UNSPECIFIED
},
114 { "mono16", AV_PIX_FMT_GRAY16
, AVCHROMA_LOC_UNSPECIFIED
},
115 { "mono12", AV_PIX_FMT_GRAY12
, AVCHROMA_LOC_UNSPECIFIED
},
116 { "mono10", AV_PIX_FMT_GRAY10
, AVCHROMA_LOC_UNSPECIFIED
},
117 { "mono9", AV_PIX_FMT_GRAY9
, AVCHROMA_LOC_UNSPECIFIED
},
118 { "mono", AV_PIX_FMT_GRAY8
, AVCHROMA_LOC_UNSPECIFIED
},
120 for (i
= 0; i
< FF_ARRAY_ELEMS(pix_fmt_array
); i
++) {
121 if (av_strstart(tokstart
, pix_fmt_array
[i
].name
, NULL
)) {
122 pix_fmt
= pix_fmt_array
[i
].pix_fmt
;
123 if (pix_fmt_array
[i
].chroma_loc
!= AVCHROMA_LOC_UNSPECIFIED
)
124 chroma_sample_location
= pix_fmt_array
[i
].chroma_loc
;
128 if (i
== FF_ARRAY_ELEMS(pix_fmt_array
)) {
129 av_log(s
, AV_LOG_ERROR
, "YUV4MPEG stream contains an unknown "
131 return AVERROR_INVALIDDATA
;
133 while (tokstart
< header_end
&& *tokstart
!= 0x20)
137 case 'I': // Interlace type
138 switch (*tokstart
++){
140 field_order
= AV_FIELD_UNKNOWN
;
143 field_order
= AV_FIELD_PROGRESSIVE
;
146 field_order
= AV_FIELD_TT
;
149 field_order
= AV_FIELD_BB
;
152 av_log(s
, AV_LOG_ERROR
, "YUV4MPEG stream contains mixed "
153 "interlaced and non-interlaced frames.\n");
155 av_log(s
, AV_LOG_ERROR
, "YUV4MPEG has invalid header.\n");
156 return AVERROR(EINVAL
);
159 case 'F': // Frame rate
160 sscanf(tokstart
, "%d:%d", &raten
, &rated
); // 0:0 if unknown
161 while (tokstart
< header_end
&& *tokstart
!= 0x20)
164 case 'A': // Pixel aspect
165 sscanf(tokstart
, "%d:%d", &aspectn
, &aspectd
); // 0:0 if unknown
166 while (tokstart
< header_end
&& *tokstart
!= 0x20)
169 case 'X': // Vendor extensions
170 if (strncmp("YSCSS=", tokstart
, 6) == 0) {
171 static const struct {
172 #define MAX_PIX_FMT_LENGTH 8
173 char name
[MAX_PIX_FMT_LENGTH
+ 1];
174 #undef MAX_PIX_FMT_LENGTH
175 enum AVPixelFormat pix_fmt
;
176 } pix_fmt_array
[] = {
177 { "420JPEG", AV_PIX_FMT_YUV420P
},
178 { "420MPEG2", AV_PIX_FMT_YUV420P
},
179 { "420PALDV", AV_PIX_FMT_YUV420P
},
180 { "420P9", AV_PIX_FMT_YUV420P9
},
181 { "422P9", AV_PIX_FMT_YUV422P9
},
182 { "444P9", AV_PIX_FMT_YUV444P9
},
183 { "420P10", AV_PIX_FMT_YUV420P10
},
184 { "444P10", AV_PIX_FMT_YUV444P10
},
185 { "420P12", AV_PIX_FMT_YUV420P12
},
186 { "422P12", AV_PIX_FMT_YUV422P12
},
187 { "444P12", AV_PIX_FMT_YUV444P12
},
188 { "420P14", AV_PIX_FMT_YUV420P14
},
189 { "422P14", AV_PIX_FMT_YUV422P14
},
190 { "444P14", AV_PIX_FMT_YUV444P14
},
191 { "420P16", AV_PIX_FMT_YUV420P16
},
192 { "422P16", AV_PIX_FMT_YUV422P16
},
193 { "444P16", AV_PIX_FMT_YUV444P16
},
194 { "411", AV_PIX_FMT_YUV411P
},
195 { "422", AV_PIX_FMT_YUV422P
},
196 { "444", AV_PIX_FMT_YUV444P
},
198 // Older nonstandard pixel format representation
200 for (size_t i
= 0; i
< FF_ARRAY_ELEMS(pix_fmt_array
); i
++)
201 if (av_strstart(tokstart
, pix_fmt_array
[i
].name
, NULL
)) {
202 alt_pix_fmt
= pix_fmt_array
[i
].pix_fmt
;
205 } else if (strncmp("COLORRANGE=", tokstart
, 11) == 0) {
207 if (strncmp("FULL",tokstart
, 4) == 0)
208 color_range
= AVCOL_RANGE_JPEG
;
209 else if (strncmp("LIMITED", tokstart
, 7) == 0)
210 color_range
= AVCOL_RANGE_MPEG
;
212 while (tokstart
< header_end
&& *tokstart
!= 0x20)
218 if (width
== -1 || height
== -1) {
219 av_log(s
, AV_LOG_ERROR
, "YUV4MPEG has invalid header.\n");
220 return AVERROR_INVALIDDATA
;
223 if (pix_fmt
== AV_PIX_FMT_NONE
) {
224 if (alt_pix_fmt
== AV_PIX_FMT_NONE
)
225 pix_fmt
= AV_PIX_FMT_YUV420P
;
227 pix_fmt
= alt_pix_fmt
;
230 if (raten
<= 0 || rated
<= 0) {
231 // Frame rate unknown
236 if (aspectn
== 0 && aspectd
== 0) {
237 // Pixel aspect unknown
241 st
= avformat_new_stream(s
, NULL
);
243 return AVERROR(ENOMEM
);
244 st
->codecpar
->width
= width
;
245 st
->codecpar
->height
= height
;
246 av_reduce(&raten
, &rated
, raten
, rated
, (1UL << 31) - 1);
247 avpriv_set_pts_info(st
, 64, rated
, raten
);
248 st
->avg_frame_rate
= av_inv_q(st
->time_base
);
249 st
->codecpar
->format
= pix_fmt
;
250 st
->codecpar
->codec_type
= AVMEDIA_TYPE_VIDEO
;
251 st
->codecpar
->codec_id
= AV_CODEC_ID_RAWVIDEO
;
252 st
->sample_aspect_ratio
= (AVRational
){ aspectn
, aspectd
};
253 st
->codecpar
->chroma_location
= chroma_sample_location
;
254 st
->codecpar
->color_range
= color_range
;
255 st
->codecpar
->field_order
= field_order
;
256 s
->packet_size
= av_image_get_buffer_size(st
->codecpar
->format
, width
, height
, 1) + Y4M_FRAME_MAGIC_LEN
;
257 if ((int) s
->packet_size
< 0)
258 return s
->packet_size
;
259 ffformatcontext(s
)->data_offset
= data_offset
= avio_tell(pb
);
261 st
->duration
= (avio_size(pb
) - data_offset
) / s
->packet_size
;
266 static int yuv4_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
269 char header
[MAX_FRAME_HEADER
+1];
271 int64_t off
= avio_tell(s
->pb
);
273 for (i
= 0; i
< MAX_FRAME_HEADER
; i
++) {
274 header
[i
] = avio_r8(s
->pb
);
275 if (header
[i
] == '\n') {
282 else if (s
->pb
->eof_reached
)
284 else if (i
== MAX_FRAME_HEADER
)
285 return AVERROR_INVALIDDATA
;
287 if (strncmp(header
, Y4M_FRAME_MAGIC
, strlen(Y4M_FRAME_MAGIC
)))
288 return AVERROR_INVALIDDATA
;
290 ret
= av_get_packet(s
->pb
, pkt
, s
->packet_size
- Y4M_FRAME_MAGIC_LEN
);
293 else if (ret
!= s
->packet_size
- Y4M_FRAME_MAGIC_LEN
) {
294 return s
->pb
->eof_reached
? AVERROR_EOF
: AVERROR_INVALIDDATA
;
296 pkt
->stream_index
= 0;
297 pkt
->pts
= (off
- ffformatcontext(s
)->data_offset
) / s
->packet_size
;
302 static int yuv4_read_seek(AVFormatContext
*s
, int stream_index
,
303 int64_t pts
, int flags
)
307 if (flags
& AVSEEK_FLAG_BACKWARD
)
308 pts
= FFMAX(0, pts
- 1);
311 pos
= pts
* s
->packet_size
;
313 if (avio_seek(s
->pb
, pos
+ ffformatcontext(s
)->data_offset
, SEEK_SET
) < 0)
318 static int yuv4_probe(const AVProbeData
*pd
)
320 /* check file header */
321 if (strncmp(pd
->buf
, Y4M_MAGIC
, sizeof(Y4M_MAGIC
) - 1) == 0)
322 return AVPROBE_SCORE_MAX
;
327 const FFInputFormat ff_yuv4mpegpipe_demuxer
= {
328 .p
.name
= "yuv4mpegpipe",
329 .p
.long_name
= NULL_IF_CONFIG_SMALL("YUV4MPEG pipe"),
330 .p
.extensions
= "y4m",
331 .read_probe
= yuv4_probe
,
332 .read_header
= yuv4_read_header
,
333 .read_packet
= yuv4_read_packet
,
334 .read_seek
= yuv4_read_seek
,