ffmpeg / libavformat / yuv4mpeg.c @ 56a10009
History | View | Annotate | Download (12.8 KB)
1 |
/*
|
---|---|
2 |
* YUV4MPEG format
|
3 |
* Copyright (c) 2001, 2002, 2003 Fabrice Bellard
|
4 |
*
|
5 |
* This file is part of Libav.
|
6 |
*
|
7 |
* Libav 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 |
* Libav 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 Libav; if not, write to the Free Software
|
19 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 |
*/
|
21 |
#include "avformat.h" |
22 |
|
23 |
#define Y4M_MAGIC "YUV4MPEG2" |
24 |
#define Y4M_FRAME_MAGIC "FRAME" |
25 |
#define Y4M_LINE_MAX 256 |
26 |
|
27 |
struct frame_attributes {
|
28 |
int interlaced_frame;
|
29 |
int top_field_first;
|
30 |
}; |
31 |
|
32 |
#if CONFIG_YUV4MPEGPIPE_MUXER
|
33 |
static int yuv4_generate_header(AVFormatContext *s, char* buf) |
34 |
{ |
35 |
AVStream *st; |
36 |
int width, height;
|
37 |
int raten, rated, aspectn, aspectd, n;
|
38 |
char inter;
|
39 |
const char *colorspace = ""; |
40 |
|
41 |
st = s->streams[0];
|
42 |
width = st->codec->width; |
43 |
height = st->codec->height; |
44 |
|
45 |
av_reduce(&raten, &rated, st->codec->time_base.den, st->codec->time_base.num, (1UL<<31)-1); |
46 |
|
47 |
aspectn = st->sample_aspect_ratio.num; |
48 |
aspectd = st->sample_aspect_ratio.den; |
49 |
|
50 |
if ( aspectn == 0 && aspectd == 1 ) aspectd = 0; // 0:0 means unknown |
51 |
|
52 |
inter = 'p'; /* progressive is the default */ |
53 |
if (st->codec->coded_frame && st->codec->coded_frame->interlaced_frame) {
|
54 |
inter = st->codec->coded_frame->top_field_first ? 't' : 'b'; |
55 |
} |
56 |
|
57 |
switch(st->codec->pix_fmt) {
|
58 |
case PIX_FMT_GRAY8:
|
59 |
colorspace = " Cmono";
|
60 |
break;
|
61 |
case PIX_FMT_YUV411P:
|
62 |
colorspace = " C411 XYSCSS=411";
|
63 |
break;
|
64 |
case PIX_FMT_YUV420P:
|
65 |
colorspace = (st->codec->chroma_sample_location == AVCHROMA_LOC_TOPLEFT)?" C420paldv XYSCSS=420PALDV":
|
66 |
(st->codec->chroma_sample_location == AVCHROMA_LOC_LEFT) ?" C420mpeg2 XYSCSS=420MPEG2":
|
67 |
" C420jpeg XYSCSS=420JPEG";
|
68 |
break;
|
69 |
case PIX_FMT_YUV422P:
|
70 |
colorspace = " C422 XYSCSS=422";
|
71 |
break;
|
72 |
case PIX_FMT_YUV444P:
|
73 |
colorspace = " C444 XYSCSS=444";
|
74 |
break;
|
75 |
} |
76 |
|
77 |
/* construct stream header, if this is the first frame */
|
78 |
n = snprintf(buf, Y4M_LINE_MAX, "%s W%d H%d F%d:%d I%c A%d:%d%s\n",
|
79 |
Y4M_MAGIC, |
80 |
width, |
81 |
height, |
82 |
raten, rated, |
83 |
inter, |
84 |
aspectn, aspectd, |
85 |
colorspace); |
86 |
|
87 |
return n;
|
88 |
} |
89 |
|
90 |
static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt) |
91 |
{ |
92 |
AVStream *st = s->streams[pkt->stream_index]; |
93 |
AVIOContext *pb = s->pb; |
94 |
AVPicture *picture; |
95 |
int* first_pkt = s->priv_data;
|
96 |
int width, height, h_chroma_shift, v_chroma_shift;
|
97 |
int i, m;
|
98 |
char buf2[Y4M_LINE_MAX+1]; |
99 |
char buf1[20]; |
100 |
uint8_t *ptr, *ptr1, *ptr2; |
101 |
|
102 |
picture = (AVPicture *)pkt->data; |
103 |
|
104 |
/* for the first packet we have to output the header as well */
|
105 |
if (*first_pkt) {
|
106 |
*first_pkt = 0;
|
107 |
if (yuv4_generate_header(s, buf2) < 0) { |
108 |
av_log(s, AV_LOG_ERROR, "Error. YUV4MPEG stream header write failed.\n");
|
109 |
return AVERROR(EIO);
|
110 |
} else {
|
111 |
avio_write(pb, buf2, strlen(buf2)); |
112 |
} |
113 |
} |
114 |
|
115 |
/* construct frame header */
|
116 |
|
117 |
m = snprintf(buf1, sizeof(buf1), "%s\n", Y4M_FRAME_MAGIC); |
118 |
avio_write(pb, buf1, strlen(buf1)); |
119 |
|
120 |
width = st->codec->width; |
121 |
height = st->codec->height; |
122 |
|
123 |
ptr = picture->data[0];
|
124 |
for(i=0;i<height;i++) { |
125 |
avio_write(pb, ptr, width); |
126 |
ptr += picture->linesize[0];
|
127 |
} |
128 |
|
129 |
if (st->codec->pix_fmt != PIX_FMT_GRAY8){
|
130 |
// Adjust for smaller Cb and Cr planes
|
131 |
avcodec_get_chroma_sub_sample(st->codec->pix_fmt, &h_chroma_shift, &v_chroma_shift); |
132 |
width >>= h_chroma_shift; |
133 |
height >>= v_chroma_shift; |
134 |
|
135 |
ptr1 = picture->data[1];
|
136 |
ptr2 = picture->data[2];
|
137 |
for(i=0;i<height;i++) { /* Cb */ |
138 |
avio_write(pb, ptr1, width); |
139 |
ptr1 += picture->linesize[1];
|
140 |
} |
141 |
for(i=0;i<height;i++) { /* Cr */ |
142 |
avio_write(pb, ptr2, width); |
143 |
ptr2 += picture->linesize[2];
|
144 |
} |
145 |
} |
146 |
avio_flush(pb); |
147 |
return 0; |
148 |
} |
149 |
|
150 |
static int yuv4_write_header(AVFormatContext *s) |
151 |
{ |
152 |
int* first_pkt = s->priv_data;
|
153 |
|
154 |
if (s->nb_streams != 1) |
155 |
return AVERROR(EIO);
|
156 |
|
157 |
if (s->streams[0]->codec->pix_fmt == PIX_FMT_YUV411P) { |
158 |
av_log(s, AV_LOG_ERROR, "Warning: generating rarely used 4:1:1 YUV stream, some mjpegtools might not work.\n");
|
159 |
} |
160 |
else if ((s->streams[0]->codec->pix_fmt != PIX_FMT_YUV420P) && |
161 |
(s->streams[0]->codec->pix_fmt != PIX_FMT_YUV422P) &&
|
162 |
(s->streams[0]->codec->pix_fmt != PIX_FMT_GRAY8) &&
|
163 |
(s->streams[0]->codec->pix_fmt != PIX_FMT_YUV444P)) {
|
164 |
av_log(s, AV_LOG_ERROR, "ERROR: yuv4mpeg only handles yuv444p, yuv422p, yuv420p, yuv411p and gray pixel formats. Use -pix_fmt to select one.\n");
|
165 |
return AVERROR(EIO);
|
166 |
} |
167 |
|
168 |
*first_pkt = 1;
|
169 |
return 0; |
170 |
} |
171 |
|
172 |
AVOutputFormat ff_yuv4mpegpipe_muxer = { |
173 |
"yuv4mpegpipe",
|
174 |
NULL_IF_CONFIG_SMALL("YUV4MPEG pipe format"),
|
175 |
"",
|
176 |
"y4m",
|
177 |
sizeof(int), |
178 |
CODEC_ID_NONE, |
179 |
CODEC_ID_RAWVIDEO, |
180 |
yuv4_write_header, |
181 |
yuv4_write_packet, |
182 |
.flags = AVFMT_RAWPICTURE, |
183 |
}; |
184 |
#endif
|
185 |
|
186 |
/* Header size increased to allow room for optional flags */
|
187 |
#define MAX_YUV4_HEADER 80 |
188 |
#define MAX_FRAME_HEADER 80 |
189 |
|
190 |
static int yuv4_read_header(AVFormatContext *s, AVFormatParameters *ap) |
191 |
{ |
192 |
char header[MAX_YUV4_HEADER+10]; // Include headroom for the longest option |
193 |
char *tokstart,*tokend,*header_end;
|
194 |
int i;
|
195 |
AVIOContext *pb = s->pb; |
196 |
int width=-1, height=-1, raten=0, rated=0, aspectn=0, aspectd=0; |
197 |
enum PixelFormat pix_fmt=PIX_FMT_NONE,alt_pix_fmt=PIX_FMT_NONE;
|
198 |
enum AVChromaLocation chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED;
|
199 |
AVStream *st; |
200 |
struct frame_attributes *s1 = s->priv_data;
|
201 |
|
202 |
for (i=0; i<MAX_YUV4_HEADER; i++) { |
203 |
header[i] = avio_r8(pb); |
204 |
if (header[i] == '\n') { |
205 |
header[i+1] = 0x20; // Add a space after last option. Makes parsing "444" vs "444alpha" easier. |
206 |
header[i+2] = 0; |
207 |
break;
|
208 |
} |
209 |
} |
210 |
if (i == MAX_YUV4_HEADER) return -1; |
211 |
if (strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC))) return -1; |
212 |
|
213 |
s1->interlaced_frame = 0;
|
214 |
s1->top_field_first = 0;
|
215 |
header_end = &header[i+1]; // Include space |
216 |
for(tokstart = &header[strlen(Y4M_MAGIC) + 1]; tokstart < header_end; tokstart++) { |
217 |
if (*tokstart==0x20) continue; |
218 |
switch (*tokstart++) {
|
219 |
case 'W': // Width. Required. |
220 |
width = strtol(tokstart, &tokend, 10);
|
221 |
tokstart=tokend; |
222 |
break;
|
223 |
case 'H': // Height. Required. |
224 |
height = strtol(tokstart, &tokend, 10);
|
225 |
tokstart=tokend; |
226 |
break;
|
227 |
case 'C': // Color space |
228 |
if (strncmp("420jpeg",tokstart,7)==0) { |
229 |
pix_fmt = PIX_FMT_YUV420P; |
230 |
chroma_sample_location = AVCHROMA_LOC_CENTER; |
231 |
} else if (strncmp("420mpeg2",tokstart,8)==0) { |
232 |
pix_fmt = PIX_FMT_YUV420P; |
233 |
chroma_sample_location = AVCHROMA_LOC_LEFT; |
234 |
} else if (strncmp("420paldv", tokstart, 8)==0) { |
235 |
pix_fmt = PIX_FMT_YUV420P; |
236 |
chroma_sample_location = AVCHROMA_LOC_TOPLEFT; |
237 |
} else if (strncmp("411", tokstart, 3)==0) |
238 |
pix_fmt = PIX_FMT_YUV411P; |
239 |
else if (strncmp("422", tokstart, 3)==0) |
240 |
pix_fmt = PIX_FMT_YUV422P; |
241 |
else if (strncmp("444alpha", tokstart, 8)==0) { |
242 |
av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 YUV4MPEG stream.\n");
|
243 |
return -1; |
244 |
} else if (strncmp("444", tokstart, 3)==0) |
245 |
pix_fmt = PIX_FMT_YUV444P; |
246 |
else if (strncmp("mono",tokstart, 4)==0) { |
247 |
pix_fmt = PIX_FMT_GRAY8; |
248 |
} else {
|
249 |
av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown pixel format.\n");
|
250 |
return -1; |
251 |
} |
252 |
while(tokstart<header_end&&*tokstart!=0x20) tokstart++; |
253 |
break;
|
254 |
case 'I': // Interlace type |
255 |
switch (*tokstart++){
|
256 |
case '?': |
257 |
break;
|
258 |
case 'p': |
259 |
s1->interlaced_frame=0;
|
260 |
break;
|
261 |
case 't': |
262 |
s1->interlaced_frame=1;
|
263 |
s1->top_field_first=1;
|
264 |
break;
|
265 |
case 'b': |
266 |
s1->interlaced_frame=1;
|
267 |
s1->top_field_first=0;
|
268 |
break;
|
269 |
case 'm': |
270 |
av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed interlaced and non-interlaced frames.\n");
|
271 |
return -1; |
272 |
default:
|
273 |
av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
|
274 |
return -1; |
275 |
} |
276 |
break;
|
277 |
case 'F': // Frame rate |
278 |
sscanf(tokstart,"%d:%d",&raten,&rated); // 0:0 if unknown |
279 |
while(tokstart<header_end&&*tokstart!=0x20) tokstart++; |
280 |
break;
|
281 |
case 'A': // Pixel aspect |
282 |
sscanf(tokstart,"%d:%d",&aspectn,&aspectd); // 0:0 if unknown |
283 |
while(tokstart<header_end&&*tokstart!=0x20) tokstart++; |
284 |
break;
|
285 |
case 'X': // Vendor extensions |
286 |
if (strncmp("YSCSS=",tokstart,6)==0) { |
287 |
// Older nonstandard pixel format representation
|
288 |
tokstart+=6;
|
289 |
if (strncmp("420JPEG",tokstart,7)==0) |
290 |
alt_pix_fmt=PIX_FMT_YUV420P; |
291 |
else if (strncmp("420MPEG2",tokstart,8)==0) |
292 |
alt_pix_fmt=PIX_FMT_YUV420P; |
293 |
else if (strncmp("420PALDV",tokstart,8)==0) |
294 |
alt_pix_fmt=PIX_FMT_YUV420P; |
295 |
else if (strncmp("411",tokstart,3)==0) |
296 |
alt_pix_fmt=PIX_FMT_YUV411P; |
297 |
else if (strncmp("422",tokstart,3)==0) |
298 |
alt_pix_fmt=PIX_FMT_YUV422P; |
299 |
else if (strncmp("444",tokstart,3)==0) |
300 |
alt_pix_fmt=PIX_FMT_YUV444P; |
301 |
} |
302 |
while(tokstart<header_end&&*tokstart!=0x20) tokstart++; |
303 |
break;
|
304 |
} |
305 |
} |
306 |
|
307 |
if ((width == -1) || (height == -1)) { |
308 |
av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
|
309 |
return -1; |
310 |
} |
311 |
|
312 |
if (pix_fmt == PIX_FMT_NONE) {
|
313 |
if (alt_pix_fmt == PIX_FMT_NONE)
|
314 |
pix_fmt = PIX_FMT_YUV420P; |
315 |
else
|
316 |
pix_fmt = alt_pix_fmt; |
317 |
} |
318 |
|
319 |
if (raten <= 0 || rated <= 0) { |
320 |
// Frame rate unknown
|
321 |
raten = 25;
|
322 |
rated = 1;
|
323 |
} |
324 |
|
325 |
if (aspectn == 0 && aspectd == 0) { |
326 |
// Pixel aspect unknown
|
327 |
aspectd = 1;
|
328 |
} |
329 |
|
330 |
st = av_new_stream(s, 0);
|
331 |
if(!st)
|
332 |
return AVERROR(ENOMEM);
|
333 |
st->codec->width = width; |
334 |
st->codec->height = height; |
335 |
av_reduce(&raten, &rated, raten, rated, (1UL<<31)-1); |
336 |
av_set_pts_info(st, 64, rated, raten);
|
337 |
st->codec->pix_fmt = pix_fmt; |
338 |
st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
339 |
st->codec->codec_id = CODEC_ID_RAWVIDEO; |
340 |
st->sample_aspect_ratio= (AVRational){aspectn, aspectd}; |
341 |
st->codec->chroma_sample_location = chroma_sample_location; |
342 |
|
343 |
return 0; |
344 |
} |
345 |
|
346 |
static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt) |
347 |
{ |
348 |
int i;
|
349 |
char header[MAX_FRAME_HEADER+1]; |
350 |
int packet_size, width, height;
|
351 |
AVStream *st = s->streams[0];
|
352 |
struct frame_attributes *s1 = s->priv_data;
|
353 |
|
354 |
for (i=0; i<MAX_FRAME_HEADER; i++) { |
355 |
header[i] = avio_r8(s->pb); |
356 |
if (header[i] == '\n') { |
357 |
header[i+1] = 0; |
358 |
break;
|
359 |
} |
360 |
} |
361 |
if (i == MAX_FRAME_HEADER) return -1; |
362 |
if (strncmp(header, Y4M_FRAME_MAGIC, strlen(Y4M_FRAME_MAGIC))) return -1; |
363 |
|
364 |
width = st->codec->width; |
365 |
height = st->codec->height; |
366 |
|
367 |
packet_size = avpicture_get_size(st->codec->pix_fmt, width, height); |
368 |
if (packet_size < 0) |
369 |
return -1; |
370 |
|
371 |
if (av_get_packet(s->pb, pkt, packet_size) != packet_size)
|
372 |
return AVERROR(EIO);
|
373 |
|
374 |
if (s->streams[0]->codec->coded_frame) { |
375 |
s->streams[0]->codec->coded_frame->interlaced_frame = s1->interlaced_frame;
|
376 |
s->streams[0]->codec->coded_frame->top_field_first = s1->top_field_first;
|
377 |
} |
378 |
|
379 |
pkt->stream_index = 0;
|
380 |
return 0; |
381 |
} |
382 |
|
383 |
static int yuv4_probe(AVProbeData *pd) |
384 |
{ |
385 |
/* check file header */
|
386 |
if (strncmp(pd->buf, Y4M_MAGIC, sizeof(Y4M_MAGIC)-1)==0) |
387 |
return AVPROBE_SCORE_MAX;
|
388 |
else
|
389 |
return 0; |
390 |
} |
391 |
|
392 |
#if CONFIG_YUV4MPEGPIPE_DEMUXER
|
393 |
AVInputFormat ff_yuv4mpegpipe_demuxer = { |
394 |
"yuv4mpegpipe",
|
395 |
NULL_IF_CONFIG_SMALL("YUV4MPEG pipe format"),
|
396 |
sizeof(struct frame_attributes), |
397 |
yuv4_probe, |
398 |
yuv4_read_header, |
399 |
yuv4_read_packet, |
400 |
.extensions = "y4m"
|
401 |
}; |
402 |
#endif
|