00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "avformat.h"
00022
00023 #define Y4M_MAGIC "YUV4MPEG2"
00024 #define Y4M_FRAME_MAGIC "FRAME"
00025 #define Y4M_LINE_MAX 256
00026
00027 struct frame_attributes {
00028 int interlaced_frame;
00029 int top_field_first;
00030 };
00031
00032 #if CONFIG_YUV4MPEGPIPE_MUXER
00033 static int yuv4_generate_header(AVFormatContext *s, char* buf)
00034 {
00035 AVStream *st;
00036 int width, height;
00037 int raten, rated, aspectn, aspectd, n;
00038 char inter;
00039 const char *colorspace = "";
00040
00041 st = s->streams[0];
00042 width = st->codec->width;
00043 height = st->codec->height;
00044
00045 av_reduce(&raten, &rated, st->codec->time_base.den, st->codec->time_base.num, (1UL<<31)-1);
00046
00047 aspectn = st->sample_aspect_ratio.num;
00048 aspectd = st->sample_aspect_ratio.den;
00049
00050 if ( aspectn == 0 && aspectd == 1 ) aspectd = 0;
00051
00052 inter = 'p';
00053 if (st->codec->coded_frame && st->codec->coded_frame->interlaced_frame) {
00054 inter = st->codec->coded_frame->top_field_first ? 't' : 'b';
00055 }
00056
00057 switch(st->codec->pix_fmt) {
00058 case PIX_FMT_GRAY8:
00059 colorspace = " Cmono";
00060 break;
00061 case PIX_FMT_YUV411P:
00062 colorspace = " C411 XYSCSS=411";
00063 break;
00064 case PIX_FMT_YUV420P:
00065 colorspace = (st->codec->chroma_sample_location == AVCHROMA_LOC_TOPLEFT)?" C420paldv XYSCSS=420PALDV":
00066 (st->codec->chroma_sample_location == AVCHROMA_LOC_LEFT) ?" C420mpeg2 XYSCSS=420MPEG2":
00067 " C420jpeg XYSCSS=420JPEG";
00068 break;
00069 case PIX_FMT_YUV422P:
00070 colorspace = " C422 XYSCSS=422";
00071 break;
00072 case PIX_FMT_YUV444P:
00073 colorspace = " C444 XYSCSS=444";
00074 break;
00075 }
00076
00077
00078 n = snprintf(buf, Y4M_LINE_MAX, "%s W%d H%d F%d:%d I%c A%d:%d%s\n",
00079 Y4M_MAGIC,
00080 width,
00081 height,
00082 raten, rated,
00083 inter,
00084 aspectn, aspectd,
00085 colorspace);
00086
00087 return n;
00088 }
00089
00090 static int yuv4_write_packet(AVFormatContext *s, AVPacket *pkt)
00091 {
00092 AVStream *st = s->streams[pkt->stream_index];
00093 AVIOContext *pb = s->pb;
00094 AVPicture *picture;
00095 int* first_pkt = s->priv_data;
00096 int width, height, h_chroma_shift, v_chroma_shift;
00097 int i;
00098 char buf2[Y4M_LINE_MAX+1];
00099 char buf1[20];
00100 uint8_t *ptr, *ptr1, *ptr2;
00101
00102 picture = (AVPicture *)pkt->data;
00103
00104
00105 if (*first_pkt) {
00106 *first_pkt = 0;
00107 if (yuv4_generate_header(s, buf2) < 0) {
00108 av_log(s, AV_LOG_ERROR, "Error. YUV4MPEG stream header write failed.\n");
00109 return AVERROR(EIO);
00110 } else {
00111 avio_write(pb, buf2, strlen(buf2));
00112 }
00113 }
00114
00115
00116
00117 snprintf(buf1, sizeof(buf1), "%s\n", Y4M_FRAME_MAGIC);
00118 avio_write(pb, buf1, strlen(buf1));
00119
00120 width = st->codec->width;
00121 height = st->codec->height;
00122
00123 ptr = picture->data[0];
00124 for(i=0;i<height;i++) {
00125 avio_write(pb, ptr, width);
00126 ptr += picture->linesize[0];
00127 }
00128
00129 if (st->codec->pix_fmt != PIX_FMT_GRAY8){
00130
00131 avcodec_get_chroma_sub_sample(st->codec->pix_fmt, &h_chroma_shift, &v_chroma_shift);
00132 width >>= h_chroma_shift;
00133 height >>= v_chroma_shift;
00134
00135 ptr1 = picture->data[1];
00136 ptr2 = picture->data[2];
00137 for(i=0;i<height;i++) {
00138 avio_write(pb, ptr1, width);
00139 ptr1 += picture->linesize[1];
00140 }
00141 for(i=0;i<height;i++) {
00142 avio_write(pb, ptr2, width);
00143 ptr2 += picture->linesize[2];
00144 }
00145 }
00146 avio_flush(pb);
00147 return 0;
00148 }
00149
00150 static int yuv4_write_header(AVFormatContext *s)
00151 {
00152 int* first_pkt = s->priv_data;
00153
00154 if (s->nb_streams != 1)
00155 return AVERROR(EIO);
00156
00157 if (s->streams[0]->codec->codec_id != CODEC_ID_RAWVIDEO) {
00158 av_log(s, AV_LOG_ERROR,
00159 "A non-rawvideo stream was selected, but yuv4mpeg only handles rawvideo streams\n");
00160 return AVERROR(EINVAL);
00161 }
00162
00163 if (s->streams[0]->codec->pix_fmt == PIX_FMT_YUV411P) {
00164 av_log(s, AV_LOG_ERROR, "Warning: generating rarely used 4:1:1 YUV stream, some mjpegtools might not work.\n");
00165 }
00166 else if ((s->streams[0]->codec->pix_fmt != PIX_FMT_YUV420P) &&
00167 (s->streams[0]->codec->pix_fmt != PIX_FMT_YUV422P) &&
00168 (s->streams[0]->codec->pix_fmt != PIX_FMT_GRAY8) &&
00169 (s->streams[0]->codec->pix_fmt != PIX_FMT_YUV444P)) {
00170 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");
00171 return AVERROR(EIO);
00172 }
00173
00174 *first_pkt = 1;
00175 return 0;
00176 }
00177
00178 AVOutputFormat ff_yuv4mpegpipe_muxer = {
00179 "yuv4mpegpipe",
00180 NULL_IF_CONFIG_SMALL("YUV4MPEG pipe format"),
00181 "",
00182 "y4m",
00183 sizeof(int),
00184 CODEC_ID_NONE,
00185 CODEC_ID_RAWVIDEO,
00186 yuv4_write_header,
00187 yuv4_write_packet,
00188 .flags = AVFMT_RAWPICTURE,
00189 };
00190 #endif
00191
00192
00193 #define MAX_YUV4_HEADER 80
00194 #define MAX_FRAME_HEADER 80
00195
00196 static int yuv4_read_header(AVFormatContext *s, AVFormatParameters *ap)
00197 {
00198 char header[MAX_YUV4_HEADER+10];
00199 char *tokstart,*tokend,*header_end;
00200 int i;
00201 AVIOContext *pb = s->pb;
00202 int width=-1, height=-1, raten=0, rated=0, aspectn=0, aspectd=0;
00203 enum PixelFormat pix_fmt=PIX_FMT_NONE,alt_pix_fmt=PIX_FMT_NONE;
00204 enum AVChromaLocation chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED;
00205 AVStream *st;
00206 struct frame_attributes *s1 = s->priv_data;
00207
00208 for (i=0; i<MAX_YUV4_HEADER; i++) {
00209 header[i] = avio_r8(pb);
00210 if (header[i] == '\n') {
00211 header[i+1] = 0x20;
00212 header[i+2] = 0;
00213 break;
00214 }
00215 }
00216 if (i == MAX_YUV4_HEADER) return -1;
00217 if (strncmp(header, Y4M_MAGIC, strlen(Y4M_MAGIC))) return -1;
00218
00219 s1->interlaced_frame = 0;
00220 s1->top_field_first = 0;
00221 header_end = &header[i+1];
00222 for(tokstart = &header[strlen(Y4M_MAGIC) + 1]; tokstart < header_end; tokstart++) {
00223 if (*tokstart==0x20) continue;
00224 switch (*tokstart++) {
00225 case 'W':
00226 width = strtol(tokstart, &tokend, 10);
00227 tokstart=tokend;
00228 break;
00229 case 'H':
00230 height = strtol(tokstart, &tokend, 10);
00231 tokstart=tokend;
00232 break;
00233 case 'C':
00234 if (strncmp("420jpeg",tokstart,7)==0) {
00235 pix_fmt = PIX_FMT_YUV420P;
00236 chroma_sample_location = AVCHROMA_LOC_CENTER;
00237 } else if (strncmp("420mpeg2",tokstart,8)==0) {
00238 pix_fmt = PIX_FMT_YUV420P;
00239 chroma_sample_location = AVCHROMA_LOC_LEFT;
00240 } else if (strncmp("420paldv", tokstart, 8)==0) {
00241 pix_fmt = PIX_FMT_YUV420P;
00242 chroma_sample_location = AVCHROMA_LOC_TOPLEFT;
00243 } else if (strncmp("411", tokstart, 3)==0)
00244 pix_fmt = PIX_FMT_YUV411P;
00245 else if (strncmp("422", tokstart, 3)==0)
00246 pix_fmt = PIX_FMT_YUV422P;
00247 else if (strncmp("444alpha", tokstart, 8)==0) {
00248 av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 YUV4MPEG stream.\n");
00249 return -1;
00250 } else if (strncmp("444", tokstart, 3)==0)
00251 pix_fmt = PIX_FMT_YUV444P;
00252 else if (strncmp("mono",tokstart, 4)==0) {
00253 pix_fmt = PIX_FMT_GRAY8;
00254 } else {
00255 av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown pixel format.\n");
00256 return -1;
00257 }
00258 while(tokstart<header_end&&*tokstart!=0x20) tokstart++;
00259 break;
00260 case 'I':
00261 switch (*tokstart++){
00262 case '?':
00263 break;
00264 case 'p':
00265 s1->interlaced_frame=0;
00266 break;
00267 case 't':
00268 s1->interlaced_frame=1;
00269 s1->top_field_first=1;
00270 break;
00271 case 'b':
00272 s1->interlaced_frame=1;
00273 s1->top_field_first=0;
00274 break;
00275 case 'm':
00276 av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed interlaced and non-interlaced frames.\n");
00277 return -1;
00278 default:
00279 av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
00280 return -1;
00281 }
00282 break;
00283 case 'F':
00284 sscanf(tokstart,"%d:%d",&raten,&rated);
00285 while(tokstart<header_end&&*tokstart!=0x20) tokstart++;
00286 break;
00287 case 'A':
00288 sscanf(tokstart,"%d:%d",&aspectn,&aspectd);
00289 while(tokstart<header_end&&*tokstart!=0x20) tokstart++;
00290 break;
00291 case 'X':
00292 if (strncmp("YSCSS=",tokstart,6)==0) {
00293
00294 tokstart+=6;
00295 if (strncmp("420JPEG",tokstart,7)==0)
00296 alt_pix_fmt=PIX_FMT_YUV420P;
00297 else if (strncmp("420MPEG2",tokstart,8)==0)
00298 alt_pix_fmt=PIX_FMT_YUV420P;
00299 else if (strncmp("420PALDV",tokstart,8)==0)
00300 alt_pix_fmt=PIX_FMT_YUV420P;
00301 else if (strncmp("411",tokstart,3)==0)
00302 alt_pix_fmt=PIX_FMT_YUV411P;
00303 else if (strncmp("422",tokstart,3)==0)
00304 alt_pix_fmt=PIX_FMT_YUV422P;
00305 else if (strncmp("444",tokstart,3)==0)
00306 alt_pix_fmt=PIX_FMT_YUV444P;
00307 }
00308 while(tokstart<header_end&&*tokstart!=0x20) tokstart++;
00309 break;
00310 }
00311 }
00312
00313 if ((width == -1) || (height == -1)) {
00314 av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
00315 return -1;
00316 }
00317
00318 if (pix_fmt == PIX_FMT_NONE) {
00319 if (alt_pix_fmt == PIX_FMT_NONE)
00320 pix_fmt = PIX_FMT_YUV420P;
00321 else
00322 pix_fmt = alt_pix_fmt;
00323 }
00324
00325 if (raten <= 0 || rated <= 0) {
00326
00327 raten = 25;
00328 rated = 1;
00329 }
00330
00331 if (aspectn == 0 && aspectd == 0) {
00332
00333 aspectd = 1;
00334 }
00335
00336 st = av_new_stream(s, 0);
00337 if(!st)
00338 return AVERROR(ENOMEM);
00339 st->codec->width = width;
00340 st->codec->height = height;
00341 av_reduce(&raten, &rated, raten, rated, (1UL<<31)-1);
00342 av_set_pts_info(st, 64, rated, raten);
00343 st->codec->pix_fmt = pix_fmt;
00344 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00345 st->codec->codec_id = CODEC_ID_RAWVIDEO;
00346 st->sample_aspect_ratio= (AVRational){aspectn, aspectd};
00347 st->codec->chroma_sample_location = chroma_sample_location;
00348
00349 return 0;
00350 }
00351
00352 static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt)
00353 {
00354 int i;
00355 char header[MAX_FRAME_HEADER+1];
00356 int packet_size, width, height;
00357 AVStream *st = s->streams[0];
00358 struct frame_attributes *s1 = s->priv_data;
00359
00360 for (i=0; i<MAX_FRAME_HEADER; i++) {
00361 header[i] = avio_r8(s->pb);
00362 if (header[i] == '\n') {
00363 header[i+1] = 0;
00364 break;
00365 }
00366 }
00367 if (i == MAX_FRAME_HEADER) return -1;
00368 if (strncmp(header, Y4M_FRAME_MAGIC, strlen(Y4M_FRAME_MAGIC))) return -1;
00369
00370 width = st->codec->width;
00371 height = st->codec->height;
00372
00373 packet_size = avpicture_get_size(st->codec->pix_fmt, width, height);
00374 if (packet_size < 0)
00375 return -1;
00376
00377 if (av_get_packet(s->pb, pkt, packet_size) != packet_size)
00378 return AVERROR(EIO);
00379
00380 if (s->streams[0]->codec->coded_frame) {
00381 s->streams[0]->codec->coded_frame->interlaced_frame = s1->interlaced_frame;
00382 s->streams[0]->codec->coded_frame->top_field_first = s1->top_field_first;
00383 }
00384
00385 pkt->stream_index = 0;
00386 return 0;
00387 }
00388
00389 static int yuv4_probe(AVProbeData *pd)
00390 {
00391
00392 if (strncmp(pd->buf, Y4M_MAGIC, sizeof(Y4M_MAGIC)-1)==0)
00393 return AVPROBE_SCORE_MAX;
00394 else
00395 return 0;
00396 }
00397
00398 #if CONFIG_YUV4MPEGPIPE_DEMUXER
00399 AVInputFormat ff_yuv4mpegpipe_demuxer = {
00400 "yuv4mpegpipe",
00401 NULL_IF_CONFIG_SMALL("YUV4MPEG pipe format"),
00402 sizeof(struct frame_attributes),
00403 yuv4_probe,
00404 yuv4_read_header,
00405 yuv4_read_packet,
00406 .extensions = "y4m"
00407 };
00408 #endif