Imported Upstream version 1.4
[deb_x265.git] / source / input / y4m.cpp
CommitLineData
72b9787e
JB
1/*****************************************************************************
2 * Copyright (C) 2013 x265 project
3 *
4 * Authors: Steve Borho <steve@borho.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
19 *
20 * This program is also available under a commercial proprietary license.
21 * For more information, contact us at license @ x265.com.
22 *****************************************************************************/
23
24#include "y4m.h"
25#include "common.h"
26
27#include <iostream>
28
29#define ENABLE_THREADING 1
30
31#if _WIN32
32#include <io.h>
33#include <fcntl.h>
34#if defined(_MSC_VER)
35#pragma warning(disable: 4996) // POSIX setmode and fileno deprecated
36#endif
37#endif
38
39using namespace x265;
40using namespace std;
41
42static const char header[] = "FRAME";
43
44Y4MInput::Y4MInput(InputFileInfo& info)
45{
46 for (int i = 0; i < QUEUE_SIZE; i++)
47 buf[i] = NULL;
48
49 readCount.set(0);
50 writeCount.set(0);
51
52 threadActive = false;
53 colorSpace = info.csp;
54 sarWidth = info.sarWidth;
55 sarHeight = info.sarHeight;
56 width = info.width;
57 height = info.height;
58 rateNum = info.fpsNum;
59 rateDenom = info.fpsDenom;
60 depth = info.depth;
61 framesize = 0;
62
63 ifs = NULL;
64 if (!strcmp(info.filename, "-"))
65 {
66 ifs = &cin;
67#if _WIN32
68 setmode(fileno(stdin), O_BINARY);
69#endif
70 }
71 else
72 ifs = new ifstream(info.filename, ios::binary | ios::in);
73
74 if (ifs && ifs->good() && parseHeader())
75 {
76 int pixelbytes = depth > 8 ? 2 : 1;
77 for (int i = 0; i < x265_cli_csps[colorSpace].planes; i++)
78 {
79 int stride = (width >> x265_cli_csps[colorSpace].width[i]) * pixelbytes;
80 framesize += (stride * (height >> x265_cli_csps[colorSpace].height[i]));
81 }
82
83 threadActive = true;
84 for (int q = 0; q < QUEUE_SIZE; q++)
85 {
86 buf[q] = X265_MALLOC(char, framesize);
87 if (!buf[q])
88 {
89 x265_log(NULL, X265_LOG_ERROR, "y4m: buffer allocation failure, aborting");
90 threadActive = false;
91 break;
92 }
93 }
94 }
95 if (!threadActive)
96 {
97 if (ifs && ifs != &cin)
98 delete ifs;
99 ifs = NULL;
100 return;
101 }
102
103 info.width = width;
104 info.height = height;
105 info.sarHeight = sarHeight;
106 info.sarWidth = sarWidth;
107 info.fpsNum = rateNum;
108 info.fpsDenom = rateDenom;
109 info.csp = colorSpace;
110 info.depth = depth;
111 info.frameCount = -1;
112
113 size_t estFrameSize = framesize + strlen(header) + 1; /* assume basic FRAME\n headers */
114
115 /* try to estimate frame count, if this is not stdin */
116 if (ifs != &cin)
117 {
118 istream::pos_type cur = ifs->tellg();
119
120#if defined(_MSC_VER) && _MSC_VER < 1700
121 /* Older MSVC versions cannot handle 64bit file sizes properly, so go native */
122 HANDLE hFile = CreateFileA(info.filename, GENERIC_READ,
123 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
124 FILE_ATTRIBUTE_NORMAL, NULL);
125 if (hFile != INVALID_HANDLE_VALUE)
126 {
127 LARGE_INTEGER size;
128 if (GetFileSizeEx(hFile, &size))
129 info.frameCount = (int)((size.QuadPart - (int64_t)cur) / estFrameSize);
130 CloseHandle(hFile);
131 }
132#else // if defined(_MSC_VER) && _MSC_VER < 1700
133 if (cur >= 0)
134 {
135 ifs->seekg(0, ios::end);
136 istream::pos_type size = ifs->tellg();
137 ifs->seekg(cur, ios::beg);
138 if (size > 0)
139 info.frameCount = (int)((size - cur) / estFrameSize);
140 }
141#endif // if defined(_MSC_VER) && _MSC_VER < 1700
142 }
143
144 if (info.skipFrames)
145 {
146#if X86_64
147 ifs->seekg((uint64_t)estFrameSize * info.skipFrames, ios::cur);
148#else
149 for (int i = 0; i < info.skipFrames; i++)
150 ifs->ignore(estFrameSize);
151#endif
152 }
153}
154
155Y4MInput::~Y4MInput()
156{
157 if (ifs && ifs != &cin)
158 delete ifs;
159
160 for (int i = 0; i < QUEUE_SIZE; i++)
161 X265_FREE(buf[i]);
162}
163
164void Y4MInput::release()
165{
166 threadActive = false;
167 readCount.set(readCount.get()); // unblock file reader
168 stop();
169 delete this;
170}
171
172bool Y4MInput::parseHeader()
173{
174 if (!ifs)
175 return false;
176
177 int csp = 0;
178 int d = 0;
179
180 while (!ifs->eof())
181 {
182 // Skip Y4MPEG string
183 int c = ifs->get();
184 while (!ifs->eof() && (c != ' ') && (c != '\n'))
185 {
186 c = ifs->get();
187 }
188
189 while (c == ' ' && !ifs->eof())
190 {
191 // read parameter identifier
192 switch (ifs->get())
193 {
194 case 'W':
195 width = 0;
196 while (!ifs->eof())
197 {
198 c = ifs->get();
199
200 if (c == ' ' || c == '\n')
201 {
202 break;
203 }
204 else
205 {
206 width = width * 10 + (c - '0');
207 }
208 }
209
210 break;
211
212 case 'H':
213 height = 0;
214 while (!ifs->eof())
215 {
216 c = ifs->get();
217 if (c == ' ' || c == '\n')
218 {
219 break;
220 }
221 else
222 {
223 height = height * 10 + (c - '0');
224 }
225 }
226
227 break;
228
229 case 'F':
230 rateNum = 0;
231 rateDenom = 0;
232 while (!ifs->eof())
233 {
234 c = ifs->get();
235 if (c == '.')
236 {
237 rateDenom = 1;
238 while (!ifs->eof())
239 {
240 c = ifs->get();
241 if (c == ' ' || c == '\n')
242 {
243 break;
244 }
245 else
246 {
247 rateNum = rateNum * 10 + (c - '0');
248 rateDenom = rateDenom * 10;
249 }
250 }
251
252 break;
253 }
254 else if (c == ':')
255 {
256 while (!ifs->eof())
257 {
258 c = ifs->get();
259 if (c == ' ' || c == '\n')
260 {
261 break;
262 }
263 else
264 rateDenom = rateDenom * 10 + (c - '0');
265 }
266
267 break;
268 }
269 else
270 {
271 rateNum = rateNum * 10 + (c - '0');
272 }
273 }
274
275 break;
276
277 case 'A':
278 sarWidth = 0;
279 sarHeight = 0;
280 while (!ifs->eof())
281 {
282 c = ifs->get();
283 if (c == ':')
284 {
285 while (!ifs->eof())
286 {
287 c = ifs->get();
288 if (c == ' ' || c == '\n')
289 {
290 break;
291 }
292 else
293 sarHeight = sarHeight * 10 + (c - '0');
294 }
295
296 break;
297 }
298 else
299 {
300 sarWidth = sarWidth * 10 + (c - '0');
301 }
302 }
303
304 break;
305
306 case 'C':
307 csp = 0;
308 d = 0;
309 while (!ifs->eof())
310 {
311 c = ifs->get();
312
313 if (c <= '9' && c >= '0')
314 {
315 csp = csp * 10 + (c - '0');
316 }
317 else if (c == 'p')
318 {
319 // example: C420p16
320 while (!ifs->eof())
321 {
322 c = ifs->get();
323
324 if (c <= '9' && c >= '0')
325 d = d * 10 + (c - '0');
326 else
327 break;
328 }
329 break;
330 }
331 else
332 break;
333 }
334
335 if (d >= 8 && d <= 16)
336 depth = d;
337 colorSpace = (csp == 444) ? X265_CSP_I444 : (csp == 422) ? X265_CSP_I422 : X265_CSP_I420;
338 break;
339
340 default:
341 while (!ifs->eof())
342 {
343 // consume this unsupported configuration word
344 c = ifs->get();
345 if (c == ' ' || c == '\n')
346 break;
347 }
348
349 break;
350 }
351 }
352
353 if (c == '\n')
354 {
355 break;
356 }
357 }
358
359 if (width < MIN_FRAME_WIDTH || width > MAX_FRAME_WIDTH ||
360 height < MIN_FRAME_HEIGHT || height > MAX_FRAME_HEIGHT ||
361 (rateNum / rateDenom) < 1 || (rateNum / rateDenom) > MAX_FRAME_RATE ||
362 colorSpace <= X265_CSP_I400 || colorSpace >= X265_CSP_COUNT)
363 return false;
364
365 return true;
366}
367
368void Y4MInput::startReader()
369{
370#if ENABLE_THREADING
371 if (threadActive)
372 start();
373#endif
374}
375
376void Y4MInput::threadMain()
377{
378 do
379 {
380 if (!populateFrameQueue())
381 break;
382 }
383 while (threadActive);
384
385 threadActive = false;
386 writeCount.set(writeCount.get()); // unblock readPicture
387}
388
389bool Y4MInput::populateFrameQueue()
390{
391 if (!ifs || ifs->fail())
392 return false;
393
394 /* strip off the FRAME header */
395 char hbuf[sizeof(header)];
396
397 ifs->read(hbuf, strlen(header));
398 if (ifs->eof())
399 return false;
400
401 if (!ifs->good() || memcmp(hbuf, header, strlen(header)))
402 {
403 x265_log(NULL, X265_LOG_ERROR, "y4m: frame header missing\n");
404 return false;
405 }
406
407 /* consume bytes up to line feed */
408 int c = ifs->get();
409 while (c != '\n' && ifs->good())
410 c = ifs->get();
411
412 /* wait for room in the ring buffer */
413 int written = writeCount.get();
414 int read = readCount.get();
415 while (written - read > QUEUE_SIZE - 2)
416 {
417 read = readCount.waitForChange(read);
418 if (!threadActive)
419 return false;
420 }
421
422 ifs->read(buf[written % QUEUE_SIZE], framesize);
423 if (ifs->good())
424 {
425 writeCount.incr();
426 return true;
427 }
428 else
429 return false;
430}
431
432bool Y4MInput::readPicture(x265_picture& pic)
433{
434 int read = readCount.get();
435 int written = writeCount.get();
436
437#if ENABLE_THREADING
438
439 /* only wait if the read thread is still active */
440 while (threadActive && read == written)
441 written = writeCount.waitForChange(written);
442
443#else
444
445 populateFrameQueue();
446
447#endif // if ENABLE_THREADING
448
449 if (read < written)
450 {
451 int pixelbytes = depth > 8 ? 2 : 1;
452 pic.bitDepth = depth;
453 pic.colorSpace = colorSpace;
454 pic.stride[0] = width * pixelbytes;
455 pic.stride[1] = pic.stride[0] >> x265_cli_csps[colorSpace].width[1];
456 pic.stride[2] = pic.stride[0] >> x265_cli_csps[colorSpace].width[2];
457 pic.planes[0] = buf[read % QUEUE_SIZE];
458 pic.planes[1] = (char*)pic.planes[0] + pic.stride[0] * height;
459 pic.planes[2] = (char*)pic.planes[1] + pic.stride[1] * (height >> x265_cli_csps[colorSpace].height[1]);
460 readCount.incr();
461 return true;
462 }
463 else
464 return false;
465}
466