Commit | Line | Data |
---|---|---|
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 | ||
39 | using namespace x265; | |
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 | 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 | ||
155 | Y4MInput::~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 | ||
164 | void Y4MInput::release() | |
165 | { | |
166 | threadActive = false; | |
167 | readCount.set(readCount.get()); // unblock file reader | |
168 | stop(); | |
169 | delete this; | |
170 | } | |
171 | ||
172 | bool 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 | ||
368 | void Y4MInput::startReader() | |
369 | { | |
370 | #if ENABLE_THREADING | |
371 | if (threadActive) | |
372 | start(); | |
373 | #endif | |
374 | } | |
375 | ||
376 | void 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 | ||
389 | bool 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 | ||
432 | bool 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 |