0
|
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
|
|
39 using namespace X265_NS;
|
|
40 using namespace std;
|
|
41
|
|
42 static const char header[] = "FRAME";
|
|
43
|
|
44 Y4MInput::Y4MInput(InputFileInfo& info)
|
|
45 {
|
|
46 for (int i = 0; i < QUEUE_SIZE; i++)
|
|
47 buf[i] = NULL;
|
|
48
|
|
49 threadActive = false;
|
|
50 colorSpace = info.csp;
|
|
51 sarWidth = info.sarWidth;
|
|
52 sarHeight = info.sarHeight;
|
|
53 width = info.width;
|
|
54 height = info.height;
|
|
55 rateNum = info.fpsNum;
|
|
56 rateDenom = info.fpsDenom;
|
|
57 depth = info.depth;
|
|
58 framesize = 0;
|
|
59
|
|
60 ifs = NULL;
|
|
61 if (!strcmp(info.filename, "-"))
|
|
62 {
|
|
63 ifs = &cin;
|
|
64 #if _WIN32
|
|
65 setmode(fileno(stdin), O_BINARY);
|
|
66 #endif
|
|
67 }
|
|
68 else
|
|
69 ifs = new ifstream(info.filename, ios::binary | ios::in);
|
|
70
|
|
71 if (ifs && ifs->good() && parseHeader())
|
|
72 {
|
|
73 int pixelbytes = depth > 8 ? 2 : 1;
|
|
74 for (int i = 0; i < x265_cli_csps[colorSpace].planes; i++)
|
|
75 {
|
|
76 int stride = (width >> x265_cli_csps[colorSpace].width[i]) * pixelbytes;
|
|
77 framesize += (stride * (height >> x265_cli_csps[colorSpace].height[i]));
|
|
78 }
|
|
79
|
|
80 threadActive = true;
|
|
81 for (int q = 0; q < QUEUE_SIZE; q++)
|
|
82 {
|
|
83 buf[q] = X265_MALLOC(char, framesize);
|
|
84 if (!buf[q])
|
|
85 {
|
|
86 x265_log(NULL, X265_LOG_ERROR, "y4m: buffer allocation failure, aborting");
|
|
87 threadActive = false;
|
|
88 break;
|
|
89 }
|
|
90 }
|
|
91 }
|
|
92 if (!threadActive)
|
|
93 {
|
|
94 if (ifs && ifs != &cin)
|
|
95 delete ifs;
|
|
96 ifs = NULL;
|
|
97 return;
|
|
98 }
|
|
99
|
|
100 info.width = width;
|
|
101 info.height = height;
|
|
102 info.sarHeight = sarHeight;
|
|
103 info.sarWidth = sarWidth;
|
|
104 info.fpsNum = rateNum;
|
|
105 info.fpsDenom = rateDenom;
|
|
106 info.csp = colorSpace;
|
|
107 info.depth = depth;
|
|
108 info.frameCount = -1;
|
|
109
|
|
110 size_t estFrameSize = framesize + strlen(header) + 1; /* assume basic FRAME\n headers */
|
|
111
|
|
112 /* try to estimate frame count, if this is not stdin */
|
|
113 if (ifs != &cin)
|
|
114 {
|
|
115 istream::pos_type cur = ifs->tellg();
|
|
116
|
|
117 #if defined(_MSC_VER) && _MSC_VER < 1700
|
|
118 /* Older MSVC versions cannot handle 64bit file sizes properly, so go native */
|
|
119 HANDLE hFile = CreateFileA(info.filename, GENERIC_READ,
|
|
120 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
121 FILE_ATTRIBUTE_NORMAL, NULL);
|
|
122 if (hFile != INVALID_HANDLE_VALUE)
|
|
123 {
|
|
124 LARGE_INTEGER size;
|
|
125 if (GetFileSizeEx(hFile, &size))
|
|
126 info.frameCount = (int)((size.QuadPart - (int64_t)cur) / estFrameSize);
|
|
127 CloseHandle(hFile);
|
|
128 }
|
|
129 #else // if defined(_MSC_VER) && _MSC_VER < 1700
|
|
130 if (cur >= 0)
|
|
131 {
|
|
132 ifs->seekg(0, ios::end);
|
|
133 istream::pos_type size = ifs->tellg();
|
|
134 ifs->seekg(cur, ios::beg);
|
|
135 if (size > 0)
|
|
136 info.frameCount = (int)((size - cur) / estFrameSize);
|
|
137 }
|
|
138 #endif // if defined(_MSC_VER) && _MSC_VER < 1700
|
|
139 }
|
|
140
|
|
141 if (info.skipFrames)
|
|
142 {
|
|
143 #if X86_64
|
|
144 ifs->seekg((uint64_t)estFrameSize * info.skipFrames, ios::cur);
|
|
145 #else
|
|
146 for (int i = 0; i < info.skipFrames; i++)
|
|
147 ifs->ignore(estFrameSize);
|
|
148 #endif
|
|
149 }
|
|
150 }
|
|
151
|
|
152 Y4MInput::~Y4MInput()
|
|
153 {
|
|
154 if (ifs && ifs != &cin)
|
|
155 delete ifs;
|
|
156
|
|
157 for (int i = 0; i < QUEUE_SIZE; i++)
|
|
158 X265_FREE(buf[i]);
|
|
159 }
|
|
160
|
|
161 void Y4MInput::release()
|
|
162 {
|
|
163 threadActive = false;
|
|
164 readCount.poke();
|
|
165 stop();
|
|
166 delete this;
|
|
167 }
|
|
168
|
|
169 bool Y4MInput::parseHeader()
|
|
170 {
|
|
171 if (!ifs)
|
|
172 return false;
|
|
173
|
|
174 int csp = 0;
|
|
175 int d = 0;
|
|
176
|
|
177 while (ifs->good())
|
|
178 {
|
|
179 // Skip Y4MPEG string
|
|
180 int c = ifs->get();
|
|
181 while (ifs->good() && (c != ' ') && (c != '\n'))
|
|
182 c = ifs->get();
|
|
183
|
|
184 while (c == ' ' && ifs->good())
|
|
185 {
|
|
186 // read parameter identifier
|
|
187 switch (ifs->get())
|
|
188 {
|
|
189 case 'W':
|
|
190 width = 0;
|
|
191 while (ifs->good())
|
|
192 {
|
|
193 c = ifs->get();
|
|
194
|
|
195 if (c == ' ' || c == '\n')
|
|
196 break;
|
|
197 else
|
|
198 width = width * 10 + (c - '0');
|
|
199 }
|
|
200 break;
|
|
201
|
|
202 case 'H':
|
|
203 height = 0;
|
|
204 while (ifs->good())
|
|
205 {
|
|
206 c = ifs->get();
|
|
207 if (c == ' ' || c == '\n')
|
|
208 break;
|
|
209 else
|
|
210 height = height * 10 + (c - '0');
|
|
211 }
|
|
212 break;
|
|
213
|
|
214 case 'F':
|
|
215 rateNum = 0;
|
|
216 rateDenom = 0;
|
|
217 while (ifs->good())
|
|
218 {
|
|
219 c = ifs->get();
|
|
220 if (c == '.')
|
|
221 {
|
|
222 rateDenom = 1;
|
|
223 while (ifs->good())
|
|
224 {
|
|
225 c = ifs->get();
|
|
226 if (c == ' ' || c == '\n')
|
|
227 break;
|
|
228 else
|
|
229 {
|
|
230 rateNum = rateNum * 10 + (c - '0');
|
|
231 rateDenom = rateDenom * 10;
|
|
232 }
|
|
233 }
|
|
234 break;
|
|
235 }
|
|
236 else if (c == ':')
|
|
237 {
|
|
238 while (ifs->good())
|
|
239 {
|
|
240 c = ifs->get();
|
|
241 if (c == ' ' || c == '\n')
|
|
242 break;
|
|
243 else
|
|
244 rateDenom = rateDenom * 10 + (c - '0');
|
|
245 }
|
|
246 break;
|
|
247 }
|
|
248 else
|
|
249 rateNum = rateNum * 10 + (c - '0');
|
|
250 }
|
|
251 break;
|
|
252
|
|
253 case 'A':
|
|
254 sarWidth = 0;
|
|
255 sarHeight = 0;
|
|
256 while (ifs->good())
|
|
257 {
|
|
258 c = ifs->get();
|
|
259 if (c == ':')
|
|
260 {
|
|
261 while (ifs->good())
|
|
262 {
|
|
263 c = ifs->get();
|
|
264 if (c == ' ' || c == '\n')
|
|
265 break;
|
|
266 else
|
|
267 sarHeight = sarHeight * 10 + (c - '0');
|
|
268 }
|
|
269 break;
|
|
270 }
|
|
271 else
|
|
272 sarWidth = sarWidth * 10 + (c - '0');
|
|
273 }
|
|
274 break;
|
|
275
|
|
276 case 'C':
|
|
277 csp = 0;
|
|
278 d = 0;
|
|
279 while (ifs->good())
|
|
280 {
|
|
281 c = ifs->get();
|
|
282
|
|
283 if (c <= '9' && c >= '0')
|
|
284 csp = csp * 10 + (c - '0');
|
|
285 else if (c == 'p')
|
|
286 {
|
|
287 // example: C420p16
|
|
288 while (ifs->good())
|
|
289 {
|
|
290 c = ifs->get();
|
|
291
|
|
292 if (c <= '9' && c >= '0')
|
|
293 d = d * 10 + (c - '0');
|
|
294 else
|
|
295 break;
|
|
296 }
|
|
297 break;
|
|
298 }
|
|
299 else
|
|
300 break;
|
|
301 }
|
|
302
|
|
303 if (d >= 8 && d <= 16)
|
|
304 depth = d;
|
|
305 colorSpace = (csp == 444) ? X265_CSP_I444 : (csp == 422) ? X265_CSP_I422 : X265_CSP_I420;
|
|
306 break;
|
|
307
|
|
308 default:
|
|
309 while (ifs->good())
|
|
310 {
|
|
311 // consume this unsupported configuration word
|
|
312 c = ifs->get();
|
|
313 if (c == ' ' || c == '\n')
|
|
314 break;
|
|
315 }
|
|
316 break;
|
|
317 }
|
|
318 }
|
|
319
|
|
320 if (c == '\n')
|
|
321 break;
|
|
322 }
|
|
323
|
|
324 if (width < MIN_FRAME_WIDTH || width > MAX_FRAME_WIDTH ||
|
|
325 height < MIN_FRAME_HEIGHT || height > MAX_FRAME_HEIGHT ||
|
|
326 (rateNum / rateDenom) < 1 || (rateNum / rateDenom) > MAX_FRAME_RATE ||
|
|
327 colorSpace <= X265_CSP_I400 || colorSpace >= X265_CSP_COUNT)
|
|
328 return false;
|
|
329
|
|
330 return true;
|
|
331 }
|
|
332
|
|
333 void Y4MInput::startReader()
|
|
334 {
|
|
335 #if ENABLE_THREADING
|
|
336 if (threadActive)
|
|
337 start();
|
|
338 #endif
|
|
339 }
|
|
340
|
|
341 void Y4MInput::threadMain()
|
|
342 {
|
|
343 THREAD_NAME("Y4MRead", 0);
|
|
344 do
|
|
345 {
|
|
346 if (!populateFrameQueue())
|
|
347 break;
|
|
348 }
|
|
349 while (threadActive);
|
|
350
|
|
351 threadActive = false;
|
|
352 writeCount.poke();
|
|
353 }
|
|
354
|
|
355 bool Y4MInput::populateFrameQueue()
|
|
356 {
|
|
357 if (!ifs || ifs->fail())
|
|
358 return false;
|
|
359
|
|
360 /* strip off the FRAME header */
|
|
361 char hbuf[sizeof(header)];
|
|
362
|
|
363 ifs->read(hbuf, strlen(header));
|
|
364 if (ifs->eof())
|
|
365 return false;
|
|
366
|
|
367 if (!ifs->good() || memcmp(hbuf, header, strlen(header)))
|
|
368 {
|
|
369 x265_log(NULL, X265_LOG_ERROR, "y4m: frame header missing\n");
|
|
370 return false;
|
|
371 }
|
|
372
|
|
373 /* consume bytes up to line feed */
|
|
374 int c = ifs->get();
|
|
375 while (c != '\n' && ifs->good())
|
|
376 c = ifs->get();
|
|
377
|
|
378 /* wait for room in the ring buffer */
|
|
379 int written = writeCount.get();
|
|
380 int read = readCount.get();
|
|
381 while (written - read > QUEUE_SIZE - 2)
|
|
382 {
|
|
383 read = readCount.waitForChange(read);
|
|
384 if (!threadActive)
|
|
385 return false;
|
|
386 }
|
|
387
|
|
388 ProfileScopeEvent(frameRead);
|
|
389 ifs->read(buf[written % QUEUE_SIZE], framesize);
|
|
390 if (ifs->good())
|
|
391 {
|
|
392 writeCount.incr();
|
|
393 return true;
|
|
394 }
|
|
395 else
|
|
396 return false;
|
|
397 }
|
|
398
|
|
399 bool Y4MInput::readPicture(x265_picture& pic)
|
|
400 {
|
|
401 int read = readCount.get();
|
|
402 int written = writeCount.get();
|
|
403
|
|
404 #if ENABLE_THREADING
|
|
405
|
|
406 /* only wait if the read thread is still active */
|
|
407 while (threadActive && read == written)
|
|
408 written = writeCount.waitForChange(written);
|
|
409
|
|
410 #else
|
|
411
|
|
412 populateFrameQueue();
|
|
413
|
|
414 #endif // if ENABLE_THREADING
|
|
415
|
|
416 if (read < written)
|
|
417 {
|
|
418 int pixelbytes = depth > 8 ? 2 : 1;
|
|
419 pic.bitDepth = depth;
|
|
420 pic.colorSpace = colorSpace;
|
|
421 pic.stride[0] = width * pixelbytes;
|
|
422 pic.stride[1] = pic.stride[0] >> x265_cli_csps[colorSpace].width[1];
|
|
423 pic.stride[2] = pic.stride[0] >> x265_cli_csps[colorSpace].width[2];
|
|
424 pic.planes[0] = buf[read % QUEUE_SIZE];
|
|
425 pic.planes[1] = (char*)pic.planes[0] + pic.stride[0] * height;
|
|
426 pic.planes[2] = (char*)pic.planes[1] + pic.stride[1] * (height >> x265_cli_csps[colorSpace].height[1]);
|
|
427 readCount.incr();
|
|
428 return true;
|
|
429 }
|
|
430 else
|
|
431 return false;
|
|
432 }
|
|
433
|