]> git.sesse.net Git - ffmpeg/blob - libavformat/yuv4mpegdec.c
Combine deprecation guards where appropriate
[ffmpeg] / libavformat / yuv4mpegdec.c
1 /*
2  * YUV4MPEG demuxer
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
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("420", tokstart, 3) == 0) {
85                 pix_fmt = AV_PIX_FMT_YUV420P;
86                 chroma_sample_location = AVCHROMA_LOC_CENTER;
87             } else if (strncmp("411", tokstart, 3) == 0)
88                 pix_fmt = AV_PIX_FMT_YUV411P;
89             else if (strncmp("422", tokstart, 3) == 0)
90                 pix_fmt = AV_PIX_FMT_YUV422P;
91             else if (strncmp("444alpha", tokstart, 8) == 0 ) {
92                 av_log(s, AV_LOG_ERROR, "Cannot handle 4:4:4:4 "
93                        "YUV4MPEG stream.\n");
94                 return -1;
95             } else if (strncmp("444", tokstart, 3) == 0)
96                 pix_fmt = AV_PIX_FMT_YUV444P;
97             else if (strncmp("mono", tokstart, 4) == 0) {
98                 pix_fmt = AV_PIX_FMT_GRAY8;
99             } else {
100                 av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains an unknown "
101                        "pixel format.\n");
102                 return -1;
103             }
104             while (tokstart < header_end && *tokstart != 0x20)
105                 tokstart++;
106             break;
107         case 'I': // Interlace type
108             switch (*tokstart++){
109             case '?':
110                 field_order = AV_FIELD_UNKNOWN;
111                 break;
112             case 'p':
113                 field_order = AV_FIELD_PROGRESSIVE;
114                 break;
115             case 't':
116                 field_order = AV_FIELD_TT;
117                 break;
118             case 'b':
119                 field_order = AV_FIELD_BB;
120                 break;
121             case 'm':
122                 av_log(s, AV_LOG_ERROR, "YUV4MPEG stream contains mixed "
123                        "interlaced and non-interlaced frames.\n");
124                 return -1;
125             default:
126                 av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
127                 return -1;
128             }
129             break;
130         case 'F': // Frame rate
131             sscanf(tokstart, "%d:%d", &raten, &rated); // 0:0 if unknown
132             while (tokstart < header_end && *tokstart != 0x20)
133                 tokstart++;
134             break;
135         case 'A': // Pixel aspect
136             sscanf(tokstart, "%d:%d", &aspectn, &aspectd); // 0:0 if unknown
137             while (tokstart < header_end && *tokstart != 0x20)
138                 tokstart++;
139             break;
140         case 'X': // Vendor extensions
141             if (strncmp("YSCSS=", tokstart, 6) == 0) {
142                 // Older nonstandard pixel format representation
143                 tokstart += 6;
144                 if (strncmp("420JPEG", tokstart, 7) == 0)
145                     alt_pix_fmt = AV_PIX_FMT_YUV420P;
146                 else if (strncmp("420MPEG2", tokstart, 8) == 0)
147                     alt_pix_fmt = AV_PIX_FMT_YUV420P;
148                 else if (strncmp("420PALDV", tokstart, 8) == 0)
149                     alt_pix_fmt = AV_PIX_FMT_YUV420P;
150                 else if (strncmp("411", tokstart, 3) == 0)
151                     alt_pix_fmt = AV_PIX_FMT_YUV411P;
152                 else if (strncmp("422", tokstart, 3) == 0)
153                     alt_pix_fmt = AV_PIX_FMT_YUV422P;
154                 else if (strncmp("444", tokstart, 3) == 0)
155                     alt_pix_fmt = AV_PIX_FMT_YUV444P;
156             }
157             while (tokstart < header_end && *tokstart != 0x20)
158                 tokstart++;
159             break;
160         }
161     }
162
163     if (width == -1 || height == -1) {
164         av_log(s, AV_LOG_ERROR, "YUV4MPEG has invalid header.\n");
165         return -1;
166     }
167
168     if (pix_fmt == AV_PIX_FMT_NONE) {
169         if (alt_pix_fmt == AV_PIX_FMT_NONE)
170             pix_fmt = AV_PIX_FMT_YUV420P;
171         else
172             pix_fmt = alt_pix_fmt;
173     }
174
175     if (raten <= 0 || rated <= 0) {
176         // Frame rate unknown
177         raten = 25;
178         rated = 1;
179     }
180
181     if (aspectn == 0 && aspectd == 0) {
182         // Pixel aspect unknown
183         aspectd = 1;
184     }
185
186     st = avformat_new_stream(s, NULL);
187     if (!st)
188         return AVERROR(ENOMEM);
189     st->codecpar->width  = width;
190     st->codecpar->height = height;
191     av_reduce(&raten, &rated, raten, rated, (1UL << 31) - 1);
192     avpriv_set_pts_info(st, 64, rated, raten);
193     st->avg_frame_rate            = av_inv_q(st->time_base);
194     st->codecpar->format          = pix_fmt;
195     st->codecpar->codec_type      = AVMEDIA_TYPE_VIDEO;
196     st->codecpar->codec_id        = AV_CODEC_ID_RAWVIDEO;
197     st->sample_aspect_ratio       = (AVRational){ aspectn, aspectd };
198     st->codecpar->chroma_location = chroma_sample_location;
199     st->codecpar->field_order     = field_order;
200
201     return 0;
202 }
203
204 static int yuv4_read_packet(AVFormatContext *s, AVPacket *pkt)
205 {
206     int i;
207     char header[MAX_FRAME_HEADER+1];
208     int packet_size, width, height, ret;
209     AVStream *st = s->streams[0];
210
211     for (i = 0; i < MAX_FRAME_HEADER; i++) {
212         header[i] = avio_r8(s->pb);
213         if (header[i] == '\n') {
214             header[i + 1] = 0;
215             break;
216         }
217     }
218     if (s->pb->error)
219         return s->pb->error;
220     else if (s->pb->eof_reached)
221         return AVERROR_EOF;
222     else if (i == MAX_FRAME_HEADER)
223         return AVERROR_INVALIDDATA;
224
225     if (strncmp(header, Y4M_FRAME_MAGIC, strlen(Y4M_FRAME_MAGIC)))
226         return AVERROR_INVALIDDATA;
227
228     width  = st->codecpar->width;
229     height = st->codecpar->height;
230
231     packet_size = av_image_get_buffer_size(st->codecpar->format,
232                                            width, height, 1);
233     if (packet_size < 0)
234         return packet_size;
235
236     ret = av_get_packet(s->pb, pkt, packet_size);
237     if (ret < 0)
238         return ret;
239     else if (ret != packet_size)
240         return s->pb->eof_reached ? AVERROR_EOF : AVERROR(EIO);
241
242     pkt->stream_index = 0;
243     return 0;
244 }
245
246 static int yuv4_probe(AVProbeData *pd)
247 {
248     /* check file header */
249     if (strncmp(pd->buf, Y4M_MAGIC, sizeof(Y4M_MAGIC) - 1) == 0)
250         return AVPROBE_SCORE_MAX;
251     else
252         return 0;
253 }
254
255 AVInputFormat ff_yuv4mpegpipe_demuxer = {
256     .name           = "yuv4mpegpipe",
257     .long_name      = NULL_IF_CONFIG_SMALL("YUV4MPEG pipe"),
258     .read_probe     = yuv4_probe,
259     .read_header    = yuv4_read_header,
260     .read_packet    = yuv4_read_packet,
261     .extensions     = "y4m",
262 };