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 "common.h" | |
25 | #include "primitives.h" | |
26 | #include "threadpool.h" | |
27 | #include "param.h" | |
28 | #include "frame.h" | |
29 | #include "framedata.h" | |
30 | #include "picyuv.h" | |
31 | ||
32 | #include "bitcost.h" | |
33 | #include "encoder.h" | |
34 | #include "slicetype.h" | |
35 | #include "frameencoder.h" | |
36 | #include "ratecontrol.h" | |
37 | #include "dpb.h" | |
38 | #include "nal.h" | |
39 | ||
40 | #include "x265.h" | |
41 | ||
42 | namespace x265 { | |
43 | const char g_sliceTypeToChar[] = {'B', 'P', 'I'}; | |
44 | } | |
45 | ||
46 | static const char *summaryCSVHeader = | |
47 | "Command, Date/Time, Elapsed Time, FPS, Bitrate, " | |
48 | "Y PSNR, U PSNR, V PSNR, Global PSNR, SSIM, SSIM (dB), " | |
49 | "I count, I ave-QP, I kpbs, I-PSNR Y, I-PSNR U, I-PSNR V, I-SSIM (dB), " | |
50 | "P count, P ave-QP, P kpbs, P-PSNR Y, P-PSNR U, P-PSNR V, P-SSIM (dB), " | |
51 | "B count, B ave-QP, B kpbs, B-PSNR Y, B-PSNR U, B-PSNR V, B-SSIM (dB), " | |
52 | "Version\n"; | |
53 | ||
b53f7c52 JB |
54 | const char* defaultAnalysisFileName = "x265_analysis.dat"; |
55 | ||
72b9787e JB |
56 | using namespace x265; |
57 | ||
58 | Encoder::Encoder() | |
59 | { | |
60 | m_aborted = false; | |
61 | m_encodedFrameNum = 0; | |
62 | m_pocLast = -1; | |
63 | m_curEncoder = 0; | |
64 | m_numLumaWPFrames = 0; | |
65 | m_numChromaWPFrames = 0; | |
66 | m_numLumaWPBiFrames = 0; | |
67 | m_numChromaWPBiFrames = 0; | |
68 | m_lookahead = NULL; | |
69 | m_frameEncoder = NULL; | |
70 | m_rateControl = NULL; | |
71 | m_dpb = NULL; | |
72 | m_exportedPic = NULL; | |
73 | m_numDelayedPic = 0; | |
74 | m_outputCount = 0; | |
75 | m_csvfpt = NULL; | |
76 | m_param = NULL; | |
77 | m_cuOffsetY = NULL; | |
78 | m_cuOffsetC = NULL; | |
79 | m_buOffsetY = NULL; | |
80 | m_buOffsetC = NULL; | |
81 | m_threadPool = 0; | |
82 | m_numThreadLocalData = 0; | |
b53f7c52 | 83 | m_analysisFile = NULL; |
72b9787e JB |
84 | } |
85 | ||
86 | void Encoder::create() | |
87 | { | |
88 | if (!primitives.sad[0]) | |
89 | { | |
90 | // this should be an impossible condition when using our public API, and indicates a serious bug. | |
91 | x265_log(m_param, X265_LOG_ERROR, "Primitives must be initialized before encoder is created\n"); | |
92 | abort(); | |
93 | } | |
94 | ||
95 | x265_param* p = m_param; | |
96 | ||
97 | int rows = (p->sourceHeight + p->maxCUSize - 1) >> g_log2Size[p->maxCUSize]; | |
98 | ||
99 | // Do not allow WPP if only one row, it is pointless and unstable | |
100 | if (rows == 1) | |
101 | p->bEnableWavefront = 0; | |
102 | ||
103 | int poolThreadCount = p->poolNumThreads ? p->poolNumThreads : getCpuCount(); | |
104 | ||
105 | // Trim the thread pool if --wpp, --pme, and --pmode are disabled | |
106 | if (!p->bEnableWavefront && !p->bDistributeModeAnalysis && !p->bDistributeMotionEstimation) | |
107 | poolThreadCount = 0; | |
108 | ||
109 | if (poolThreadCount > 1) | |
110 | { | |
111 | m_threadPool = ThreadPool::allocThreadPool(poolThreadCount); | |
112 | poolThreadCount = m_threadPool->getThreadCount(); | |
113 | } | |
114 | else | |
115 | poolThreadCount = 0; | |
116 | ||
117 | if (!poolThreadCount) | |
118 | { | |
119 | // issue warnings if any of these features were requested | |
120 | if (p->bEnableWavefront) | |
121 | x265_log(p, X265_LOG_WARNING, "No thread pool allocated, --wpp disabled\n"); | |
122 | if (p->bDistributeMotionEstimation) | |
123 | x265_log(p, X265_LOG_WARNING, "No thread pool allocated, --pme disabled\n"); | |
124 | if (p->bDistributeModeAnalysis) | |
125 | x265_log(p, X265_LOG_WARNING, "No thread pool allocated, --pmode disabled\n"); | |
126 | ||
127 | // disable all pool features if the thread pool is disabled or unusable. | |
128 | p->bEnableWavefront = p->bDistributeModeAnalysis = p->bDistributeMotionEstimation = 0; | |
129 | } | |
130 | ||
131 | if (!p->frameNumThreads) | |
132 | { | |
133 | // auto-detect frame threads | |
134 | int cpuCount = getCpuCount(); | |
135 | if (!p->bEnableWavefront) | |
136 | p->frameNumThreads = X265_MIN(cpuCount, (rows + 1) / 2); | |
b53f7c52 | 137 | else if (cpuCount >= 32) |
72b9787e JB |
138 | p->frameNumThreads = 6; // dual-socket 10-core IvyBridge or higher |
139 | else if (cpuCount >= 16) | |
140 | p->frameNumThreads = 5; // 8 HT cores, or dual socket | |
141 | else if (cpuCount >= 8) | |
142 | p->frameNumThreads = 3; // 4 HT cores | |
143 | else if (cpuCount >= 4) | |
144 | p->frameNumThreads = 2; // Dual or Quad core | |
145 | else | |
146 | p->frameNumThreads = 1; | |
147 | } | |
148 | ||
149 | x265_log(p, X265_LOG_INFO, "WPP streams / frame threads / pool : %d / %d / %d%s%s\n", | |
150 | p->bEnableWavefront ? rows : 0, p->frameNumThreads, poolThreadCount, | |
151 | p->bDistributeMotionEstimation ? " / pme" : "", p->bDistributeModeAnalysis ? " / pmode" : ""); | |
152 | ||
153 | m_frameEncoder = new FrameEncoder[m_param->frameNumThreads]; | |
154 | for (int i = 0; i < m_param->frameNumThreads; i++) | |
155 | m_frameEncoder[i].setThreadPool(m_threadPool); | |
156 | ||
157 | if (!m_scalingList.init()) | |
158 | { | |
159 | x265_log(m_param, X265_LOG_ERROR, "Unable to allocate scaling list arrays\n"); | |
160 | m_aborted = true; | |
161 | } | |
162 | else if (!m_param->scalingLists || !strcmp(m_param->scalingLists, "off")) | |
163 | m_scalingList.m_bEnabled = false; | |
164 | else if (!strcmp(m_param->scalingLists, "default")) | |
165 | m_scalingList.setDefaultScalingList(); | |
166 | else if (m_scalingList.parseScalingList(m_param->scalingLists)) | |
167 | m_aborted = true; | |
168 | m_scalingList.setupQuantMatrices(); | |
169 | ||
170 | /* Allocate thread local data, one for each thread pool worker and | |
171 | * if --no-wpp, one for each frame encoder */ | |
172 | m_numThreadLocalData = poolThreadCount; | |
173 | if (!m_param->bEnableWavefront) | |
174 | m_numThreadLocalData += m_param->frameNumThreads; | |
175 | m_threadLocalData = new ThreadLocalData[m_numThreadLocalData]; | |
176 | for (int i = 0; i < m_numThreadLocalData; i++) | |
177 | { | |
178 | m_threadLocalData[i].analysis.setThreadPool(m_threadPool); | |
179 | m_threadLocalData[i].analysis.initSearch(*m_param, m_scalingList); | |
180 | m_threadLocalData[i].analysis.create(m_threadLocalData); | |
181 | } | |
182 | ||
183 | if (!m_param->bEnableWavefront) | |
184 | for (int i = 0; i < m_param->frameNumThreads; i++) | |
185 | m_frameEncoder[i].m_tld = &m_threadLocalData[poolThreadCount + i]; | |
186 | ||
187 | m_lookahead = new Lookahead(m_param, m_threadPool); | |
188 | m_dpb = new DPB(m_param); | |
189 | m_rateControl = new RateControl(m_param); | |
190 | ||
191 | initSPS(&m_sps); | |
192 | initPPS(&m_pps); | |
193 | ||
194 | /* Try to open CSV file handle */ | |
195 | if (m_param->csvfn) | |
196 | { | |
197 | m_csvfpt = fopen(m_param->csvfn, "r"); | |
198 | if (m_csvfpt) | |
199 | { | |
b53f7c52 | 200 | /* file already exists, re-open for append */ |
72b9787e JB |
201 | fclose(m_csvfpt); |
202 | m_csvfpt = fopen(m_param->csvfn, "ab"); | |
203 | } | |
204 | else | |
205 | { | |
b53f7c52 | 206 | /* new CSV file, write header */ |
72b9787e JB |
207 | m_csvfpt = fopen(m_param->csvfn, "wb"); |
208 | if (m_csvfpt) | |
209 | { | |
210 | if (m_param->logLevel >= X265_LOG_DEBUG) | |
211 | { | |
212 | fprintf(m_csvfpt, "Encode Order, Type, POC, QP, Bits, "); | |
213 | if (m_param->rc.rateControlMode == X265_RC_CRF) | |
214 | fprintf(m_csvfpt, "RateFactor, "); | |
215 | fprintf(m_csvfpt, "Y PSNR, U PSNR, V PSNR, YUV PSNR, SSIM, SSIM (dB), " | |
216 | "Encoding time, Elapsed time, List 0, List 1\n"); | |
217 | } | |
218 | else | |
219 | fputs(summaryCSVHeader, m_csvfpt); | |
220 | } | |
221 | } | |
222 | } | |
223 | ||
b53f7c52 JB |
224 | if (m_frameEncoder) |
225 | { | |
226 | int numRows = (m_param->sourceHeight + g_maxCUSize - 1) / g_maxCUSize; | |
227 | int numCols = (m_param->sourceWidth + g_maxCUSize - 1) / g_maxCUSize; | |
228 | for (int i = 0; i < m_param->frameNumThreads; i++) | |
229 | { | |
230 | if (!m_frameEncoder[i].init(this, numRows, numCols, i)) | |
231 | { | |
232 | x265_log(m_param, X265_LOG_ERROR, "Unable to initialize frame encoder, aborting\n"); | |
233 | m_aborted = true; | |
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | if (m_param->bEmitHRDSEI) | |
239 | m_rateControl->initHRD(&m_sps); | |
240 | if (!m_rateControl->init(&m_sps)) | |
241 | m_aborted = true; | |
242 | ||
243 | m_lookahead->init(); | |
244 | ||
245 | if (m_param->analysisMode) | |
246 | { | |
247 | const char* name = m_param->analysisFileName; | |
248 | if (!name) | |
249 | name = defaultAnalysisFileName; | |
250 | const char* mode = m_param->analysisMode == X265_ANALYSIS_LOAD ? "rb" : "wb"; | |
251 | m_analysisFile = fopen(name, mode); | |
252 | if (!m_analysisFile) | |
253 | { | |
254 | x265_log(NULL, X265_LOG_ERROR, "Analysis load/save: failed to open file %s\n", name); | |
255 | m_aborted = true; | |
256 | } | |
257 | } | |
258 | ||
72b9787e | 259 | m_aborted |= parseLambdaFile(m_param); |
b53f7c52 JB |
260 | |
261 | m_encodeStartTime = x265_mdate(); | |
72b9787e JB |
262 | } |
263 | ||
264 | void Encoder::destroy() | |
265 | { | |
266 | if (m_exportedPic) | |
267 | { | |
268 | ATOMIC_DEC(&m_exportedPic->m_countRefEncoders); | |
269 | m_exportedPic = NULL; | |
270 | } | |
271 | ||
272 | if (m_rateControl) | |
273 | m_rateControl->terminate(); // unblock all blocked RC calls | |
274 | ||
275 | if (m_frameEncoder) | |
276 | { | |
277 | for (int i = 0; i < m_param->frameNumThreads; i++) | |
278 | { | |
279 | // Ensure frame encoder is idle before destroying it | |
280 | m_frameEncoder[i].getEncodedPicture(m_nalList); | |
281 | m_frameEncoder[i].destroy(); | |
282 | } | |
283 | ||
284 | delete [] m_frameEncoder; | |
285 | } | |
286 | ||
287 | for (int i = 0; i < m_numThreadLocalData; i++) | |
288 | m_threadLocalData[i].destroy(); | |
289 | ||
290 | delete [] m_threadLocalData; | |
291 | ||
292 | if (m_lookahead) | |
293 | { | |
294 | m_lookahead->destroy(); | |
295 | delete m_lookahead; | |
296 | } | |
297 | ||
298 | delete m_dpb; | |
299 | if (m_rateControl) | |
300 | { | |
301 | m_rateControl->destroy(); | |
302 | delete m_rateControl; | |
303 | } | |
304 | // thread pool release should always happen last | |
305 | if (m_threadPool) | |
306 | m_threadPool->release(); | |
307 | ||
308 | X265_FREE(m_cuOffsetY); | |
309 | X265_FREE(m_cuOffsetC); | |
310 | X265_FREE(m_buOffsetY); | |
311 | X265_FREE(m_buOffsetC); | |
312 | ||
b53f7c52 JB |
313 | if (m_analysisFile) |
314 | fclose(m_analysisFile); | |
315 | free(m_param->analysisFileName); | |
316 | free(m_param->csvfn); | |
72b9787e JB |
317 | if (m_csvfpt) |
318 | fclose(m_csvfpt); | |
319 | free(m_param->rc.statFileName); // alloc'd by strdup | |
320 | ||
321 | X265_FREE(m_param); | |
322 | } | |
323 | ||
72b9787e JB |
324 | void Encoder::updateVbvPlan(RateControl* rc) |
325 | { | |
326 | for (int i = 0; i < m_param->frameNumThreads; i++) | |
327 | { | |
328 | FrameEncoder *encoder = &m_frameEncoder[i]; | |
329 | if (encoder->m_rce.isActive && encoder->m_rce.poc != rc->m_curSlice->m_poc) | |
330 | { | |
331 | int64_t bits = (int64_t) X265_MAX(encoder->m_rce.frameSizeEstimated, encoder->m_rce.frameSizePlanned); | |
332 | rc->m_bufferFill -= bits; | |
333 | rc->m_bufferFill = X265_MAX(rc->m_bufferFill, 0); | |
334 | rc->m_bufferFill += encoder->m_rce.bufferRate; | |
335 | rc->m_bufferFill = X265_MIN(rc->m_bufferFill, rc->m_bufferSize); | |
336 | if (rc->m_2pass) | |
337 | rc->m_predictedBits += bits; | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | /** | |
343 | * Feed one new input frame into the encoder, get one frame out. If pic_in is | |
344 | * NULL, a flush condition is implied and pic_in must be NULL for all subsequent | |
345 | * calls for this encoder instance. | |
346 | * | |
347 | * pic_in input original YUV picture or NULL | |
348 | * pic_out pointer to reconstructed picture struct | |
349 | * | |
350 | * returns 0 if no frames are currently available for output | |
351 | * 1 if frame was output, m_nalList contains access unit | |
352 | * negative on malloc error or abort */ | |
353 | int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out) | |
354 | { | |
355 | if (m_aborted) | |
356 | return -1; | |
357 | ||
358 | if (m_exportedPic) | |
359 | { | |
360 | ATOMIC_DEC(&m_exportedPic->m_countRefEncoders); | |
361 | m_exportedPic = NULL; | |
362 | m_dpb->recycleUnreferenced(); | |
363 | } | |
364 | ||
365 | if (pic_in) | |
366 | { | |
367 | if (pic_in->colorSpace != m_param->internalCsp) | |
368 | { | |
369 | x265_log(m_param, X265_LOG_ERROR, "Unsupported color space (%d) on input\n", | |
370 | pic_in->colorSpace); | |
371 | return -1; | |
372 | } | |
373 | if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16) | |
374 | { | |
375 | x265_log(m_param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n", | |
376 | pic_in->bitDepth); | |
377 | return -1; | |
378 | } | |
379 | ||
380 | Frame *inFrame; | |
381 | if (m_dpb->m_freeList.empty()) | |
382 | { | |
383 | inFrame = new Frame; | |
384 | if (inFrame->create(m_param)) | |
385 | { | |
386 | /* the first PicYuv created is asked to generate the CU and block unit offset | |
387 | * arrays which are then shared with all subsequent PicYuv (orig and recon) | |
388 | * allocated by this top level encoder */ | |
389 | if (m_cuOffsetY) | |
390 | { | |
b53f7c52 JB |
391 | inFrame->m_fencPic->m_cuOffsetC = m_cuOffsetC; |
392 | inFrame->m_fencPic->m_cuOffsetY = m_cuOffsetY; | |
393 | inFrame->m_fencPic->m_buOffsetC = m_buOffsetC; | |
394 | inFrame->m_fencPic->m_buOffsetY = m_buOffsetY; | |
72b9787e JB |
395 | } |
396 | else | |
397 | { | |
b53f7c52 | 398 | if (!inFrame->m_fencPic->createOffsets(m_sps)) |
72b9787e JB |
399 | { |
400 | m_aborted = true; | |
401 | x265_log(m_param, X265_LOG_ERROR, "memory allocation failure, aborting encode\n"); | |
402 | inFrame->destroy(); | |
403 | delete inFrame; | |
404 | return -1; | |
405 | } | |
406 | else | |
407 | { | |
b53f7c52 JB |
408 | m_cuOffsetC = inFrame->m_fencPic->m_cuOffsetC; |
409 | m_cuOffsetY = inFrame->m_fencPic->m_cuOffsetY; | |
410 | m_buOffsetC = inFrame->m_fencPic->m_buOffsetC; | |
411 | m_buOffsetY = inFrame->m_fencPic->m_buOffsetY; | |
72b9787e JB |
412 | } |
413 | } | |
414 | } | |
415 | else | |
416 | { | |
417 | m_aborted = true; | |
418 | x265_log(m_param, X265_LOG_ERROR, "memory allocation failure, aborting encode\n"); | |
419 | inFrame->destroy(); | |
420 | delete inFrame; | |
421 | return -1; | |
422 | } | |
423 | } | |
424 | else | |
425 | inFrame = m_dpb->m_freeList.popBack(); | |
426 | ||
427 | /* Copy input picture into a Frame and PicYuv, send to lookahead */ | |
428 | inFrame->m_poc = ++m_pocLast; | |
b53f7c52 JB |
429 | inFrame->m_fencPic->copyFromPicture(*pic_in, m_sps.conformanceWindow.rightOffset, m_sps.conformanceWindow.bottomOffset); |
430 | ||
72b9787e JB |
431 | inFrame->m_userData = pic_in->userData; |
432 | inFrame->m_pts = pic_in->pts; | |
433 | inFrame->m_forceqp = pic_in->forceqp; | |
434 | ||
435 | if (m_pocLast == 0) | |
436 | m_firstPts = inFrame->m_pts; | |
437 | if (m_bframeDelay && m_pocLast == m_bframeDelay) | |
438 | m_bframeDelayTime = inFrame->m_pts - m_firstPts; | |
439 | ||
440 | /* Encoder holds a reference count until stats collection is finished */ | |
441 | ATOMIC_INC(&inFrame->m_countRefEncoders); | |
442 | bool bEnableWP = m_param->bEnableWeightedPred || m_param->bEnableWeightedBiPred; | |
443 | if (m_param->rc.aqMode || bEnableWP) | |
444 | { | |
445 | if (m_param->rc.cuTree && m_param->rc.bStatRead) | |
446 | { | |
447 | if (!m_rateControl->cuTreeReadFor2Pass(inFrame)) | |
448 | { | |
449 | m_aborted = 1; | |
450 | return -1; | |
451 | } | |
452 | } | |
453 | else | |
454 | m_rateControl->calcAdaptiveQuantFrame(inFrame); | |
455 | } | |
456 | ||
457 | /* Use the frame types from the first pass, if available */ | |
458 | int sliceType = (m_param->rc.bStatRead) ? m_rateControl->rateControlSliceType(inFrame->m_poc) : pic_in->sliceType; | |
b53f7c52 JB |
459 | |
460 | /* In analysisSave mode, x265_analysis_data is allocated in pic_in and inFrame points to this */ | |
461 | /* Load analysis data before lookahead->addPicture, since sliceType has been decided */ | |
462 | if (m_param->analysisMode == X265_ANALYSIS_LOAD) | |
463 | { | |
464 | x265_picture* inputPic = const_cast<x265_picture*>(pic_in); | |
465 | /* readAnalysisFile reads analysis data for the frame and allocates memory based on slicetype */ | |
466 | readAnalysisFile(&inputPic->analysisData, inFrame->m_poc); | |
467 | inFrame->m_analysisData.poc = inFrame->m_poc; | |
468 | inFrame->m_analysisData.sliceType = inputPic->analysisData.sliceType; | |
469 | inFrame->m_analysisData.numCUsInFrame = inputPic->analysisData.numCUsInFrame; | |
470 | inFrame->m_analysisData.numPartitions = inputPic->analysisData.numPartitions; | |
471 | inFrame->m_analysisData.interData = inputPic->analysisData.interData; | |
472 | inFrame->m_analysisData.intraData = inputPic->analysisData.intraData; | |
473 | sliceType = inputPic->analysisData.sliceType; | |
474 | } | |
475 | ||
72b9787e JB |
476 | m_lookahead->addPicture(inFrame, sliceType); |
477 | m_numDelayedPic++; | |
478 | } | |
479 | else | |
480 | m_lookahead->flush(); | |
481 | ||
482 | FrameEncoder *curEncoder = &m_frameEncoder[m_curEncoder]; | |
483 | m_curEncoder = (m_curEncoder + 1) % m_param->frameNumThreads; | |
484 | int ret = 0; | |
485 | ||
486 | // getEncodedPicture() should block until the FrameEncoder has completed | |
487 | // encoding the frame. This is how back-pressure through the API is | |
488 | // accomplished when the encoder is full. | |
489 | Frame *outFrame = curEncoder->getEncodedPicture(m_nalList); | |
490 | ||
491 | if (outFrame) | |
492 | { | |
493 | Slice *slice = outFrame->m_encData->m_slice; | |
b53f7c52 JB |
494 | |
495 | /* Free up pic_in->analysisData since it has already been used */ | |
496 | if (m_param->analysisMode == X265_ANALYSIS_LOAD) | |
497 | freeAnalysis(&outFrame->m_analysisData); | |
498 | ||
72b9787e JB |
499 | if (pic_out) |
500 | { | |
b53f7c52 | 501 | PicYuv *recpic = outFrame->m_reconPic; |
72b9787e JB |
502 | pic_out->poc = slice->m_poc; |
503 | pic_out->bitDepth = X265_DEPTH; | |
504 | pic_out->userData = outFrame->m_userData; | |
505 | pic_out->colorSpace = m_param->internalCsp; | |
506 | ||
507 | pic_out->pts = outFrame->m_pts; | |
508 | pic_out->dts = outFrame->m_dts; | |
509 | ||
510 | switch (slice->m_sliceType) | |
511 | { | |
512 | case I_SLICE: | |
513 | pic_out->sliceType = outFrame->m_lowres.bKeyframe ? X265_TYPE_IDR : X265_TYPE_I; | |
514 | break; | |
515 | case P_SLICE: | |
516 | pic_out->sliceType = X265_TYPE_P; | |
517 | break; | |
518 | case B_SLICE: | |
519 | pic_out->sliceType = X265_TYPE_B; | |
520 | break; | |
521 | } | |
522 | ||
523 | pic_out->planes[0] = recpic->m_picOrg[0]; | |
524 | pic_out->stride[0] = (int)(recpic->m_stride * sizeof(pixel)); | |
525 | pic_out->planes[1] = recpic->m_picOrg[1]; | |
526 | pic_out->stride[1] = (int)(recpic->m_strideC * sizeof(pixel)); | |
527 | pic_out->planes[2] = recpic->m_picOrg[2]; | |
528 | pic_out->stride[2] = (int)(recpic->m_strideC * sizeof(pixel)); | |
72b9787e | 529 | |
b53f7c52 JB |
530 | /* Dump analysis data from pic_out to file in save mode and free */ |
531 | if (m_param->analysisMode == X265_ANALYSIS_SAVE) | |
532 | { | |
533 | pic_out->analysisData.poc = pic_out->poc; | |
534 | pic_out->analysisData.sliceType = pic_out->sliceType; | |
535 | pic_out->analysisData.numCUsInFrame = outFrame->m_analysisData.numCUsInFrame; | |
536 | pic_out->analysisData.numPartitions = outFrame->m_analysisData.numPartitions; | |
537 | pic_out->analysisData.interData = outFrame->m_analysisData.interData; | |
538 | pic_out->analysisData.intraData = outFrame->m_analysisData.intraData; | |
539 | writeAnalysisFile(&pic_out->analysisData); | |
540 | freeAnalysis(&pic_out->analysisData); | |
541 | } | |
72b9787e | 542 | } |
72b9787e JB |
543 | if (slice->m_sliceType == P_SLICE) |
544 | { | |
545 | if (slice->m_weightPredTable[0][0][0].bPresentFlag) | |
546 | m_numLumaWPFrames++; | |
547 | if (slice->m_weightPredTable[0][0][1].bPresentFlag || | |
548 | slice->m_weightPredTable[0][0][2].bPresentFlag) | |
549 | m_numChromaWPFrames++; | |
550 | } | |
551 | else if (slice->m_sliceType == B_SLICE) | |
552 | { | |
553 | bool bLuma = false, bChroma = false; | |
554 | for (int l = 0; l < 2; l++) | |
555 | { | |
556 | if (slice->m_weightPredTable[l][0][0].bPresentFlag) | |
557 | bLuma = true; | |
558 | if (slice->m_weightPredTable[l][0][1].bPresentFlag || | |
559 | slice->m_weightPredTable[l][0][2].bPresentFlag) | |
560 | bChroma = true; | |
561 | } | |
562 | ||
563 | if (bLuma) | |
564 | m_numLumaWPBiFrames++; | |
565 | if (bChroma) | |
566 | m_numChromaWPBiFrames++; | |
567 | } | |
568 | if (m_aborted) | |
569 | return -1; | |
72b9787e | 570 | finishFrameStats(outFrame, curEncoder, curEncoder->m_accessUnitBits); |
b53f7c52 | 571 | |
72b9787e JB |
572 | // Allow this frame to be recycled if no frame encoders are using it for reference |
573 | if (!pic_out) | |
574 | { | |
575 | ATOMIC_DEC(&outFrame->m_countRefEncoders); | |
576 | m_dpb->recycleUnreferenced(); | |
577 | } | |
578 | else | |
579 | m_exportedPic = outFrame; | |
580 | ||
581 | m_numDelayedPic--; | |
582 | ||
583 | ret = 1; | |
584 | } | |
585 | ||
586 | // pop a single frame from decided list, then provide to frame encoder | |
587 | // curEncoder is guaranteed to be idle at this point | |
588 | Frame* frameEnc = m_lookahead->getDecidedPicture(); | |
589 | if (frameEnc) | |
590 | { | |
591 | // give this picture a FrameData instance before encoding | |
592 | if (m_dpb->m_picSymFreeList) | |
593 | { | |
594 | frameEnc->m_encData = m_dpb->m_picSymFreeList; | |
595 | m_dpb->m_picSymFreeList = m_dpb->m_picSymFreeList->m_freeListNext; | |
596 | frameEnc->reinit(m_sps); | |
597 | } | |
598 | else | |
599 | { | |
600 | frameEnc->allocEncodeData(m_param, m_sps); | |
601 | Slice* slice = frameEnc->m_encData->m_slice; | |
602 | slice->m_sps = &m_sps; | |
603 | slice->m_pps = &m_pps; | |
604 | slice->m_maxNumMergeCand = m_param->maxNumMergeCand; | |
605 | slice->m_endCUAddr = slice->realEndAddress(m_sps.numCUsInFrame * NUM_CU_PARTITIONS); | |
b53f7c52 JB |
606 | frameEnc->m_reconPic->m_cuOffsetC = m_cuOffsetC; |
607 | frameEnc->m_reconPic->m_cuOffsetY = m_cuOffsetY; | |
608 | frameEnc->m_reconPic->m_buOffsetC = m_buOffsetC; | |
609 | frameEnc->m_reconPic->m_buOffsetY = m_buOffsetY; | |
72b9787e JB |
610 | } |
611 | curEncoder->m_rce.encodeOrder = m_encodedFrameNum++; | |
612 | if (m_bframeDelay) | |
613 | { | |
614 | int64_t *prevReorderedPts = m_prevReorderedPts; | |
615 | frameEnc->m_dts = m_encodedFrameNum > m_bframeDelay | |
616 | ? prevReorderedPts[(m_encodedFrameNum - m_bframeDelay) % m_bframeDelay] | |
617 | : frameEnc->m_reorderedPts - m_bframeDelayTime; | |
618 | prevReorderedPts[m_encodedFrameNum % m_bframeDelay] = frameEnc->m_reorderedPts; | |
619 | } | |
620 | else | |
621 | frameEnc->m_dts = frameEnc->m_reorderedPts; | |
b53f7c52 JB |
622 | /* Allocate analysis data before encode in save mode. This is allocated in frameEnc*/ |
623 | if (m_param->analysisMode == X265_ANALYSIS_SAVE) | |
624 | { | |
625 | x265_analysis_data* analysis = &frameEnc->m_analysisData; | |
626 | analysis->poc = frameEnc->m_poc; | |
627 | analysis->sliceType = frameEnc->m_lowres.sliceType; | |
628 | uint32_t widthInCU = (m_param->sourceWidth + g_maxCUSize - 1) >> g_maxLog2CUSize; | |
629 | uint32_t heightInCU = (m_param->sourceHeight + g_maxCUSize - 1) >> g_maxLog2CUSize; | |
630 | ||
631 | uint32_t numCUsInFrame = widthInCU * heightInCU; | |
632 | analysis->numCUsInFrame = numCUsInFrame; | |
633 | analysis->numPartitions = NUM_CU_PARTITIONS; | |
634 | allocAnalysis(analysis); | |
635 | } | |
72b9787e JB |
636 | |
637 | // determine references, setup RPS, etc | |
638 | m_dpb->prepareEncode(frameEnc); | |
639 | ||
640 | if (m_param->rc.rateControlMode != X265_RC_CQP) | |
641 | m_lookahead->getEstimatedPictureCost(frameEnc); | |
642 | ||
643 | // Allow FrameEncoder::compressFrame() to start in the frame encoder thread | |
644 | if (!curEncoder->startCompressFrame(frameEnc)) | |
645 | m_aborted = true; | |
646 | } | |
647 | else if (m_encodedFrameNum) | |
648 | m_rateControl->setFinalFrameCount(m_encodedFrameNum); | |
649 | ||
650 | return ret; | |
651 | } | |
652 | ||
653 | void EncStats::addPsnr(double psnrY, double psnrU, double psnrV) | |
654 | { | |
655 | m_psnrSumY += psnrY; | |
656 | m_psnrSumU += psnrU; | |
657 | m_psnrSumV += psnrV; | |
658 | } | |
659 | ||
660 | void EncStats::addBits(uint64_t bits) | |
661 | { | |
662 | m_accBits += bits; | |
663 | m_numPics++; | |
664 | } | |
665 | ||
666 | void EncStats::addSsim(double ssim) | |
667 | { | |
668 | m_globalSsim += ssim; | |
669 | } | |
670 | ||
671 | void EncStats::addQP(double aveQp) | |
672 | { | |
673 | m_totalQp += aveQp; | |
674 | } | |
675 | ||
676 | char* Encoder::statsCSVString(EncStats& stat, char* buffer) | |
677 | { | |
678 | if (!stat.m_numPics) | |
679 | { | |
680 | sprintf(buffer, "-, -, -, -, -, -, -, "); | |
681 | return buffer; | |
682 | } | |
683 | ||
684 | double fps = (double)m_param->fpsNum / m_param->fpsDenom; | |
685 | double scale = fps / 1000 / (double)stat.m_numPics; | |
686 | ||
687 | int len = sprintf(buffer, "%-6u, ", stat.m_numPics); | |
688 | ||
689 | len += sprintf(buffer + len, "%2.2lf, ", stat.m_totalQp / (double)stat.m_numPics); | |
690 | len += sprintf(buffer + len, "%-8.2lf, ", stat.m_accBits * scale); | |
691 | if (m_param->bEnablePsnr) | |
692 | { | |
693 | len += sprintf(buffer + len, "%.3lf, %.3lf, %.3lf, ", | |
694 | stat.m_psnrSumY / (double)stat.m_numPics, | |
695 | stat.m_psnrSumU / (double)stat.m_numPics, | |
696 | stat.m_psnrSumV / (double)stat.m_numPics); | |
697 | } | |
698 | else | |
699 | len += sprintf(buffer + len, "-, -, -, "); | |
700 | ||
701 | if (m_param->bEnableSsim) | |
702 | sprintf(buffer + len, "%.3lf, ", x265_ssim2dB(stat.m_globalSsim / (double)stat.m_numPics)); | |
703 | else | |
704 | sprintf(buffer + len, "-, "); | |
705 | return buffer; | |
706 | } | |
707 | ||
708 | char* Encoder::statsString(EncStats& stat, char* buffer) | |
709 | { | |
710 | double fps = (double)m_param->fpsNum / m_param->fpsDenom; | |
711 | double scale = fps / 1000 / (double)stat.m_numPics; | |
712 | ||
713 | int len = sprintf(buffer, "%6u, ", stat.m_numPics); | |
714 | ||
715 | len += sprintf(buffer + len, "Avg QP:%2.2lf", stat.m_totalQp / (double)stat.m_numPics); | |
716 | len += sprintf(buffer + len, " kb/s: %-8.2lf", stat.m_accBits * scale); | |
717 | if (m_param->bEnablePsnr) | |
718 | { | |
719 | len += sprintf(buffer + len, " PSNR Mean: Y:%.3lf U:%.3lf V:%.3lf", | |
720 | stat.m_psnrSumY / (double)stat.m_numPics, | |
721 | stat.m_psnrSumU / (double)stat.m_numPics, | |
722 | stat.m_psnrSumV / (double)stat.m_numPics); | |
723 | } | |
724 | if (m_param->bEnableSsim) | |
725 | { | |
726 | sprintf(buffer + len, " SSIM Mean: %.6lf (%.3lfdB)", | |
727 | stat.m_globalSsim / (double)stat.m_numPics, | |
728 | x265_ssim2dB(stat.m_globalSsim / (double)stat.m_numPics)); | |
729 | } | |
730 | return buffer; | |
731 | } | |
732 | ||
733 | void Encoder::printSummary() | |
734 | { | |
735 | if (m_param->logLevel < X265_LOG_INFO) | |
736 | return; | |
737 | ||
738 | char buffer[200]; | |
739 | if (m_analyzeI.m_numPics) | |
740 | x265_log(m_param, X265_LOG_INFO, "frame I: %s\n", statsString(m_analyzeI, buffer)); | |
741 | if (m_analyzeP.m_numPics) | |
742 | x265_log(m_param, X265_LOG_INFO, "frame P: %s\n", statsString(m_analyzeP, buffer)); | |
743 | if (m_analyzeB.m_numPics) | |
744 | x265_log(m_param, X265_LOG_INFO, "frame B: %s\n", statsString(m_analyzeB, buffer)); | |
745 | if (m_analyzeAll.m_numPics) | |
746 | x265_log(m_param, X265_LOG_INFO, "global : %s\n", statsString(m_analyzeAll, buffer)); | |
747 | if (m_param->bEnableWeightedPred && m_analyzeP.m_numPics) | |
748 | { | |
749 | x265_log(m_param, X265_LOG_INFO, "Weighted P-Frames: Y:%.1f%% UV:%.1f%%\n", | |
750 | (float)100.0 * m_numLumaWPFrames / m_analyzeP.m_numPics, | |
751 | (float)100.0 * m_numChromaWPFrames / m_analyzeP.m_numPics); | |
752 | } | |
753 | if (m_param->bEnableWeightedBiPred && m_analyzeB.m_numPics) | |
754 | { | |
755 | x265_log(m_param, X265_LOG_INFO, "Weighted B-Frames: Y:%.1f%% UV:%.1f%%\n", | |
756 | (float)100.0 * m_numLumaWPBiFrames / m_analyzeB.m_numPics, | |
757 | (float)100.0 * m_numChromaWPBiFrames / m_analyzeB.m_numPics); | |
758 | } | |
759 | int pWithB = 0; | |
760 | for (int i = 0; i <= m_param->bframes; i++) | |
761 | pWithB += m_lookahead->m_histogram[i]; | |
762 | ||
763 | if (pWithB) | |
764 | { | |
765 | int p = 0; | |
766 | for (int i = 0; i <= m_param->bframes; i++) | |
767 | p += sprintf(buffer + p, "%.1f%% ", 100. * m_lookahead->m_histogram[i] / pWithB); | |
768 | ||
769 | x265_log(m_param, X265_LOG_INFO, "consecutive B-frames: %s\n", buffer); | |
770 | } | |
771 | if (m_param->bLossless) | |
772 | { | |
773 | float frameSize = (float)(m_param->sourceWidth - m_sps.conformanceWindow.rightOffset) * | |
774 | (m_param->sourceHeight - m_sps.conformanceWindow.bottomOffset); | |
775 | float uncompressed = frameSize * X265_DEPTH * m_analyzeAll.m_numPics; | |
776 | ||
777 | x265_log(m_param, X265_LOG_INFO, "lossless compression ratio %.2f::1\n", uncompressed / m_analyzeAll.m_accBits); | |
778 | } | |
779 | ||
780 | if (!m_param->bLogCuStats) | |
781 | return; | |
782 | ||
783 | for (int sliceType = 2; sliceType >= 0; sliceType--) | |
784 | { | |
785 | if (sliceType == P_SLICE && !m_analyzeP.m_numPics) | |
786 | continue; | |
787 | if (sliceType == B_SLICE && !m_analyzeB.m_numPics) | |
788 | continue; | |
789 | ||
790 | StatisticLog finalLog; | |
791 | for (uint32_t depth = 0; depth <= g_maxCUDepth; depth++) | |
792 | { | |
793 | for (int i = 0; i < m_param->frameNumThreads; i++) | |
794 | { | |
795 | StatisticLog& enclog = m_frameEncoder[i].m_sliceTypeLog[sliceType]; | |
796 | if (!depth) | |
797 | finalLog.totalCu += enclog.totalCu; | |
798 | finalLog.cntIntra[depth] += enclog.cntIntra[depth]; | |
799 | for (int m = 0; m < INTER_MODES; m++) | |
800 | { | |
801 | if (m < INTRA_MODES) | |
802 | finalLog.cuIntraDistribution[depth][m] += enclog.cuIntraDistribution[depth][m]; | |
803 | finalLog.cuInterDistribution[depth][m] += enclog.cuInterDistribution[depth][m]; | |
804 | } | |
805 | ||
806 | if (depth == g_maxCUDepth) | |
807 | finalLog.cntIntraNxN += enclog.cntIntraNxN; | |
808 | if (sliceType != I_SLICE) | |
809 | { | |
810 | finalLog.cntTotalCu[depth] += enclog.cntTotalCu[depth]; | |
811 | finalLog.cntInter[depth] += enclog.cntInter[depth]; | |
812 | finalLog.cntSkipCu[depth] += enclog.cntSkipCu[depth]; | |
813 | } | |
814 | } | |
815 | ||
816 | uint64_t cntInter, cntSkipCu, cntIntra = 0, cntIntraNxN = 0, encCu = 0; | |
817 | uint64_t cuInterDistribution[INTER_MODES], cuIntraDistribution[INTRA_MODES]; | |
818 | ||
819 | // check for 0/0, if true assign 0 else calculate percentage | |
820 | for (int n = 0; n < INTER_MODES; n++) | |
821 | { | |
822 | if (!finalLog.cntInter[depth]) | |
823 | cuInterDistribution[n] = 0; | |
824 | else | |
825 | cuInterDistribution[n] = (finalLog.cuInterDistribution[depth][n] * 100) / finalLog.cntInter[depth]; | |
826 | ||
827 | if (n < INTRA_MODES) | |
828 | { | |
829 | if (!finalLog.cntIntra[depth]) | |
830 | { | |
831 | cntIntraNxN = 0; | |
832 | cuIntraDistribution[n] = 0; | |
833 | } | |
834 | else | |
835 | { | |
836 | cntIntraNxN = (finalLog.cntIntraNxN * 100) / finalLog.cntIntra[depth]; | |
837 | cuIntraDistribution[n] = (finalLog.cuIntraDistribution[depth][n] * 100) / finalLog.cntIntra[depth]; | |
838 | } | |
839 | } | |
840 | } | |
841 | ||
842 | if (!finalLog.totalCu) | |
843 | encCu = 0; | |
844 | else if (sliceType == I_SLICE) | |
845 | { | |
846 | cntIntra = (finalLog.cntIntra[depth] * 100) / finalLog.totalCu; | |
847 | cntIntraNxN = (finalLog.cntIntraNxN * 100) / finalLog.totalCu; | |
848 | } | |
849 | else | |
850 | encCu = ((finalLog.cntIntra[depth] + finalLog.cntInter[depth]) * 100) / finalLog.totalCu; | |
851 | ||
852 | if (sliceType == I_SLICE) | |
853 | { | |
854 | cntInter = 0; | |
855 | cntSkipCu = 0; | |
856 | } | |
857 | else if (!finalLog.cntTotalCu[depth]) | |
858 | { | |
859 | cntInter = 0; | |
860 | cntIntra = 0; | |
861 | cntSkipCu = 0; | |
862 | } | |
863 | else | |
864 | { | |
865 | cntInter = (finalLog.cntInter[depth] * 100) / finalLog.cntTotalCu[depth]; | |
866 | cntIntra = (finalLog.cntIntra[depth] * 100) / finalLog.cntTotalCu[depth]; | |
867 | cntSkipCu = (finalLog.cntSkipCu[depth] * 100) / finalLog.cntTotalCu[depth]; | |
868 | } | |
869 | ||
870 | // print statistics | |
871 | int cuSize = g_maxCUSize >> depth; | |
872 | char stats[256] = { 0 }; | |
873 | int len = 0; | |
874 | if (sliceType != I_SLICE) | |
875 | len += sprintf(stats + len, " EncCU "X265_LL "%% Merge "X265_LL "%%", encCu, cntSkipCu); | |
876 | ||
877 | if (cntInter) | |
878 | { | |
879 | len += sprintf(stats + len, " Inter "X265_LL "%%", cntInter); | |
880 | if (m_param->bEnableAMP) | |
881 | len += sprintf(stats + len, "(%dx%d "X265_LL "%% %dx%d "X265_LL "%% %dx%d "X265_LL "%% AMP "X265_LL "%%)", | |
882 | cuSize, cuSize, cuInterDistribution[0], | |
883 | cuSize / 2, cuSize, cuInterDistribution[2], | |
884 | cuSize, cuSize / 2, cuInterDistribution[1], | |
885 | cuInterDistribution[3]); | |
886 | else if (m_param->bEnableRectInter) | |
887 | len += sprintf(stats + len, "(%dx%d "X265_LL "%% %dx%d "X265_LL "%% %dx%d "X265_LL "%%)", | |
888 | cuSize, cuSize, cuInterDistribution[0], | |
889 | cuSize / 2, cuSize, cuInterDistribution[2], | |
890 | cuSize, cuSize / 2, cuInterDistribution[1]); | |
891 | } | |
892 | if (cntIntra) | |
893 | { | |
894 | len += sprintf(stats + len, " Intra "X265_LL "%%(DC "X265_LL "%% P "X265_LL "%% Ang "X265_LL "%%", | |
895 | cntIntra, cuIntraDistribution[0], | |
896 | cuIntraDistribution[1], cuIntraDistribution[2]); | |
897 | if (sliceType != I_SLICE) | |
898 | { | |
899 | if (depth == g_maxCUDepth) | |
900 | len += sprintf(stats + len, " %dx%d "X265_LL "%%", cuSize / 2, cuSize / 2, cntIntraNxN); | |
901 | } | |
902 | ||
903 | len += sprintf(stats + len, ")"); | |
904 | if (sliceType == I_SLICE) | |
905 | { | |
906 | if (depth == g_maxCUDepth) | |
907 | len += sprintf(stats + len, " %dx%d: "X265_LL "%%", cuSize / 2, cuSize / 2, cntIntraNxN); | |
908 | } | |
909 | } | |
910 | const char slicechars[] = "BPI"; | |
911 | if (stats[0]) | |
912 | x265_log(m_param, X265_LOG_INFO, "%c%-2d:%s\n", slicechars[sliceType], cuSize, stats); | |
913 | } | |
914 | } | |
915 | } | |
916 | ||
917 | void Encoder::fetchStats(x265_stats *stats, size_t statsSizeBytes) | |
918 | { | |
919 | if (statsSizeBytes >= sizeof(stats)) | |
920 | { | |
921 | stats->globalPsnrY = m_analyzeAll.m_psnrSumY; | |
922 | stats->globalPsnrU = m_analyzeAll.m_psnrSumU; | |
923 | stats->globalPsnrV = m_analyzeAll.m_psnrSumV; | |
924 | stats->encodedPictureCount = m_analyzeAll.m_numPics; | |
925 | stats->totalWPFrames = m_numLumaWPFrames; | |
926 | stats->accBits = m_analyzeAll.m_accBits; | |
927 | stats->elapsedEncodeTime = (double)(x265_mdate() - m_encodeStartTime) / 1000000; | |
928 | if (stats->encodedPictureCount > 0) | |
929 | { | |
930 | stats->globalSsim = m_analyzeAll.m_globalSsim / stats->encodedPictureCount; | |
931 | stats->globalPsnr = (stats->globalPsnrY * 6 + stats->globalPsnrU + stats->globalPsnrV) / (8 * stats->encodedPictureCount); | |
932 | stats->elapsedVideoTime = (double)stats->encodedPictureCount * m_param->fpsDenom / m_param->fpsNum; | |
933 | stats->bitrate = (0.001f * stats->accBits) / stats->elapsedVideoTime; | |
934 | } | |
935 | else | |
936 | { | |
937 | stats->globalSsim = 0; | |
938 | stats->globalPsnr = 0; | |
939 | stats->bitrate = 0; | |
940 | stats->elapsedVideoTime = 0; | |
941 | } | |
942 | } | |
943 | ||
944 | /* If new statistics are added to x265_stats, we must check here whether the | |
945 | * structure provided by the user is the new structure or an older one (for | |
946 | * future safety) */ | |
947 | } | |
948 | ||
949 | void Encoder::writeLog(int argc, char **argv) | |
950 | { | |
951 | if (m_csvfpt) | |
952 | { | |
953 | if (m_param->logLevel >= X265_LOG_DEBUG) | |
954 | { | |
955 | // adding summary to a per-frame csv log file needs a summary header | |
956 | fprintf(m_csvfpt, "\nSummary\n"); | |
957 | fputs(summaryCSVHeader, m_csvfpt); | |
958 | } | |
959 | // CLI arguments or other | |
960 | for (int i = 1; i < argc; i++) | |
961 | { | |
962 | if (i) fputc(' ', m_csvfpt); | |
963 | fputs(argv[i], m_csvfpt); | |
964 | } | |
965 | ||
966 | // current date and time | |
967 | time_t now; | |
968 | struct tm* timeinfo; | |
969 | time(&now); | |
970 | timeinfo = localtime(&now); | |
971 | char buffer[200]; | |
972 | strftime(buffer, 128, "%c", timeinfo); | |
973 | fprintf(m_csvfpt, ", %s, ", buffer); | |
974 | ||
975 | x265_stats stats; | |
976 | fetchStats(&stats, sizeof(stats)); | |
977 | ||
978 | // elapsed time, fps, bitrate | |
979 | fprintf(m_csvfpt, "%.2f, %.2f, %.2f,", | |
980 | stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate); | |
981 | ||
982 | if (m_param->bEnablePsnr) | |
983 | fprintf(m_csvfpt, " %.3lf, %.3lf, %.3lf, %.3lf,", | |
984 | stats.globalPsnrY / stats.encodedPictureCount, stats.globalPsnrU / stats.encodedPictureCount, | |
985 | stats.globalPsnrV / stats.encodedPictureCount, stats.globalPsnr); | |
986 | else | |
987 | fprintf(m_csvfpt, " -, -, -, -,"); | |
988 | if (m_param->bEnableSsim) | |
989 | fprintf(m_csvfpt, " %.6f, %6.3f,", stats.globalSsim, x265_ssim2dB(stats.globalSsim)); | |
990 | else | |
991 | fprintf(m_csvfpt, " -, -,"); | |
992 | ||
993 | fputs(statsCSVString(m_analyzeI, buffer), m_csvfpt); | |
994 | fputs(statsCSVString(m_analyzeP, buffer), m_csvfpt); | |
995 | fputs(statsCSVString(m_analyzeB, buffer), m_csvfpt); | |
996 | fprintf(m_csvfpt, " %s\n", x265_version_str); | |
997 | } | |
998 | } | |
999 | ||
1000 | /** | |
1001 | * Produce an ascii(hex) representation of picture digest. | |
1002 | * | |
1003 | * Returns: a statically allocated null-terminated string. DO NOT FREE. | |
1004 | */ | |
1005 | static const char*digestToString(const unsigned char digest[3][16], int numChar) | |
1006 | { | |
1007 | const char* hex = "0123456789abcdef"; | |
1008 | static char string[99]; | |
1009 | int cnt = 0; | |
1010 | ||
1011 | for (int yuvIdx = 0; yuvIdx < 3; yuvIdx++) | |
1012 | { | |
1013 | for (int i = 0; i < numChar; i++) | |
1014 | { | |
1015 | string[cnt++] = hex[digest[yuvIdx][i] >> 4]; | |
1016 | string[cnt++] = hex[digest[yuvIdx][i] & 0xf]; | |
1017 | } | |
1018 | ||
1019 | string[cnt++] = ','; | |
1020 | } | |
1021 | ||
1022 | string[cnt - 1] = '\0'; | |
1023 | return string; | |
1024 | } | |
1025 | ||
1026 | void Encoder::finishFrameStats(Frame* curFrame, FrameEncoder *curEncoder, uint64_t bits) | |
1027 | { | |
b53f7c52 | 1028 | PicYuv* reconPic = curFrame->m_reconPic; |
72b9787e JB |
1029 | |
1030 | //===== calculate PSNR ===== | |
1031 | int width = reconPic->m_picWidth - m_sps.conformanceWindow.rightOffset; | |
1032 | int height = reconPic->m_picHeight - m_sps.conformanceWindow.bottomOffset; | |
1033 | int size = width * height; | |
1034 | ||
1035 | int maxvalY = 255 << (X265_DEPTH - 8); | |
1036 | int maxvalC = 255 << (X265_DEPTH - 8); | |
1037 | double refValueY = (double)maxvalY * maxvalY * size; | |
1038 | double refValueC = (double)maxvalC * maxvalC * size / 4.0; | |
1039 | uint64_t ssdY, ssdU, ssdV; | |
1040 | ||
1041 | ssdY = curEncoder->m_SSDY; | |
1042 | ssdU = curEncoder->m_SSDU; | |
1043 | ssdV = curEncoder->m_SSDV; | |
1044 | double psnrY = (ssdY ? 10.0 * log10(refValueY / (double)ssdY) : 99.99); | |
1045 | double psnrU = (ssdU ? 10.0 * log10(refValueC / (double)ssdU) : 99.99); | |
1046 | double psnrV = (ssdV ? 10.0 * log10(refValueC / (double)ssdV) : 99.99); | |
1047 | ||
1048 | FrameData& curEncData = *curFrame->m_encData; | |
1049 | Slice* slice = curEncData.m_slice; | |
1050 | ||
1051 | //===== add bits, psnr and ssim ===== | |
1052 | m_analyzeAll.addBits(bits); | |
1053 | m_analyzeAll.addQP(curEncData.m_avgQpAq); | |
1054 | ||
1055 | if (m_param->bEnablePsnr) | |
1056 | m_analyzeAll.addPsnr(psnrY, psnrU, psnrV); | |
1057 | ||
1058 | double ssim = 0.0; | |
1059 | if (m_param->bEnableSsim && curEncoder->m_ssimCnt) | |
1060 | { | |
1061 | ssim = curEncoder->m_ssim / curEncoder->m_ssimCnt; | |
1062 | m_analyzeAll.addSsim(ssim); | |
1063 | } | |
1064 | if (slice->isIntra()) | |
1065 | { | |
1066 | m_analyzeI.addBits(bits); | |
1067 | m_analyzeI.addQP(curEncData.m_avgQpAq); | |
1068 | if (m_param->bEnablePsnr) | |
1069 | m_analyzeI.addPsnr(psnrY, psnrU, psnrV); | |
1070 | if (m_param->bEnableSsim) | |
1071 | m_analyzeI.addSsim(ssim); | |
1072 | } | |
1073 | else if (slice->isInterP()) | |
1074 | { | |
1075 | m_analyzeP.addBits(bits); | |
1076 | m_analyzeP.addQP(curEncData.m_avgQpAq); | |
1077 | if (m_param->bEnablePsnr) | |
1078 | m_analyzeP.addPsnr(psnrY, psnrU, psnrV); | |
1079 | if (m_param->bEnableSsim) | |
1080 | m_analyzeP.addSsim(ssim); | |
1081 | } | |
1082 | else if (slice->isInterB()) | |
1083 | { | |
1084 | m_analyzeB.addBits(bits); | |
1085 | m_analyzeB.addQP(curEncData.m_avgQpAq); | |
1086 | if (m_param->bEnablePsnr) | |
1087 | m_analyzeB.addPsnr(psnrY, psnrU, psnrV); | |
1088 | if (m_param->bEnableSsim) | |
1089 | m_analyzeB.addSsim(ssim); | |
1090 | } | |
1091 | ||
1092 | // if debug log level is enabled, per frame logging is performed | |
1093 | if (m_param->logLevel >= X265_LOG_DEBUG) | |
1094 | { | |
1095 | char c = (slice->isIntra() ? 'I' : slice->isInterP() ? 'P' : 'B'); | |
1096 | int poc = slice->m_poc; | |
1097 | if (!IS_REFERENCED(curFrame)) | |
1098 | c += 32; // lower case if unreferenced | |
1099 | ||
1100 | char buf[1024]; | |
1101 | int p; | |
1102 | p = sprintf(buf, "POC:%d %c QP %2.2lf(%d) %10d bits", poc, c, curEncData.m_avgQpAq, slice->m_sliceQp, (int)bits); | |
1103 | if (m_param->rc.rateControlMode == X265_RC_CRF) | |
1104 | p += sprintf(buf + p, " RF:%.3lf", curEncData.m_rateFactor); | |
1105 | if (m_param->bEnablePsnr) | |
1106 | p += sprintf(buf + p, " [Y:%6.2lf U:%6.2lf V:%6.2lf]", psnrY, psnrU, psnrV); | |
1107 | if (m_param->bEnableSsim) | |
1108 | p += sprintf(buf + p, " [SSIM: %.3lfdB]", x265_ssim2dB(ssim)); | |
1109 | ||
1110 | if (!slice->isIntra()) | |
1111 | { | |
1112 | int numLists = slice->isInterP() ? 1 : 2; | |
1113 | for (int list = 0; list < numLists; list++) | |
1114 | { | |
1115 | p += sprintf(buf + p, " [L%d ", list); | |
1116 | for (int ref = 0; ref < slice->m_numRefIdx[list]; ref++) | |
1117 | { | |
1118 | int k = slice->m_refPOCList[list][ref] - slice->m_lastIDR; | |
1119 | p += sprintf(buf + p, "%d ", k); | |
1120 | } | |
1121 | ||
1122 | p += sprintf(buf + p, "]"); | |
1123 | } | |
1124 | } | |
1125 | ||
1126 | // per frame CSV logging if the file handle is valid | |
1127 | if (m_csvfpt) | |
1128 | { | |
1129 | fprintf(m_csvfpt, "%d, %c-SLICE, %4d, %2.2lf, %10d,", m_outputCount++, c, poc, curEncData.m_avgQpAq, (int)bits); | |
1130 | if (m_param->rc.rateControlMode == X265_RC_CRF) | |
1131 | fprintf(m_csvfpt, "%.3lf,", curEncData.m_rateFactor); | |
1132 | double psnr = (psnrY * 6 + psnrU + psnrV) / 8; | |
1133 | if (m_param->bEnablePsnr) | |
1134 | fprintf(m_csvfpt, "%.3lf, %.3lf, %.3lf, %.3lf,", psnrY, psnrU, psnrV, psnr); | |
1135 | else | |
1136 | fprintf(m_csvfpt, " -, -, -, -,"); | |
1137 | if (m_param->bEnableSsim) | |
1138 | fprintf(m_csvfpt, " %.6f, %6.3f,", ssim, x265_ssim2dB(ssim)); | |
1139 | else | |
1140 | fprintf(m_csvfpt, " -, -,"); | |
1141 | fprintf(m_csvfpt, " %.3lf, %.3lf", curEncoder->m_frameTime, curEncoder->m_elapsedCompressTime); | |
1142 | if (!slice->isIntra()) | |
1143 | { | |
1144 | int numLists = slice->isInterP() ? 1 : 2; | |
1145 | for (int list = 0; list < numLists; list++) | |
1146 | { | |
1147 | fprintf(m_csvfpt, ", "); | |
1148 | for (int ref = 0; ref < slice->m_numRefIdx[list]; ref++) | |
1149 | { | |
1150 | int k = slice->m_refPOCList[list][ref] - slice->m_lastIDR; | |
1151 | fprintf(m_csvfpt, " %d", k); | |
1152 | } | |
1153 | } | |
1154 | ||
1155 | if (numLists == 1) | |
1156 | fprintf(m_csvfpt, ", -"); | |
1157 | } | |
1158 | else | |
1159 | fprintf(m_csvfpt, ", -, -"); | |
1160 | fprintf(m_csvfpt, "\n"); | |
1161 | } | |
1162 | ||
1163 | if (m_param->decodedPictureHashSEI && m_param->logLevel >= X265_LOG_FULL) | |
1164 | { | |
1165 | const char* digestStr = NULL; | |
1166 | if (m_param->decodedPictureHashSEI == 1) | |
1167 | { | |
1168 | digestStr = digestToString(curEncoder->m_seiReconPictureDigest.m_digest, 16); | |
1169 | p += sprintf(buf + p, " [MD5:%s]", digestStr); | |
1170 | } | |
1171 | else if (m_param->decodedPictureHashSEI == 2) | |
1172 | { | |
1173 | digestStr = digestToString(curEncoder->m_seiReconPictureDigest.m_digest, 2); | |
1174 | p += sprintf(buf + p, " [CRC:%s]", digestStr); | |
1175 | } | |
1176 | else if (m_param->decodedPictureHashSEI == 3) | |
1177 | { | |
1178 | digestStr = digestToString(curEncoder->m_seiReconPictureDigest.m_digest, 4); | |
1179 | p += sprintf(buf + p, " [Checksum:%s]", digestStr); | |
1180 | } | |
1181 | } | |
1182 | x265_log(m_param, X265_LOG_DEBUG, "%s\n", buf); | |
1183 | fflush(stderr); | |
1184 | } | |
1185 | } | |
1186 | ||
1187 | #if defined(_MSC_VER) | |
1188 | #pragma warning(disable: 4800) // forcing int to bool | |
1189 | #pragma warning(disable: 4127) // conditional expression is constant | |
1190 | #endif | |
1191 | ||
1192 | void Encoder::getStreamHeaders(NALList& list, Entropy& sbacCoder, Bitstream& bs) | |
1193 | { | |
1194 | sbacCoder.setBitstream(&bs); | |
1195 | ||
1196 | /* headers for start of bitstream */ | |
1197 | bs.resetBits(); | |
1198 | sbacCoder.codeVPS(m_vps); | |
1199 | bs.writeByteAlignment(); | |
1200 | list.serialize(NAL_UNIT_VPS, bs); | |
1201 | ||
1202 | bs.resetBits(); | |
1203 | sbacCoder.codeSPS(m_sps, m_scalingList, m_vps.ptl); | |
1204 | bs.writeByteAlignment(); | |
1205 | list.serialize(NAL_UNIT_SPS, bs); | |
1206 | ||
1207 | bs.resetBits(); | |
1208 | sbacCoder.codePPS(m_pps); | |
1209 | bs.writeByteAlignment(); | |
1210 | list.serialize(NAL_UNIT_PPS, bs); | |
1211 | ||
1212 | if (m_param->bEmitInfoSEI) | |
1213 | { | |
1214 | char *opts = x265_param2string(m_param); | |
1215 | if (opts) | |
1216 | { | |
1217 | char *buffer = X265_MALLOC(char, strlen(opts) + strlen(x265_version_str) + | |
1218 | strlen(x265_build_info_str) + 200); | |
1219 | if (buffer) | |
1220 | { | |
1221 | sprintf(buffer, "x265 (build %d) - %s:%s - H.265/HEVC codec - " | |
1222 | "Copyright 2013-2014 (c) Multicoreware Inc - " | |
1223 | "http://x265.org - options: %s", | |
1224 | X265_BUILD, x265_version_str, x265_build_info_str, opts); | |
1225 | ||
1226 | bs.resetBits(); | |
1227 | SEIuserDataUnregistered idsei; | |
1228 | idsei.m_userData = (uint8_t*)buffer; | |
1229 | idsei.m_userDataLength = (uint32_t)strlen(buffer); | |
1230 | idsei.write(bs, m_sps); | |
1231 | bs.writeByteAlignment(); | |
1232 | list.serialize(NAL_UNIT_PREFIX_SEI, bs); | |
1233 | ||
1234 | X265_FREE(buffer); | |
1235 | } | |
1236 | ||
1237 | X265_FREE(opts); | |
1238 | } | |
1239 | } | |
1240 | ||
1241 | if (m_param->bEmitHRDSEI || !!m_param->interlaceMode) | |
1242 | { | |
1243 | /* Picture Timing and Buffering Period SEI require the SPS to be "activated" */ | |
1244 | SEIActiveParameterSets sei; | |
1245 | sei.m_selfContainedCvsFlag = true; | |
1246 | sei.m_noParamSetUpdateFlag = true; | |
1247 | ||
1248 | bs.resetBits(); | |
1249 | sei.write(bs, m_sps); | |
1250 | bs.writeByteAlignment(); | |
1251 | list.serialize(NAL_UNIT_PREFIX_SEI, bs); | |
1252 | } | |
1253 | } | |
1254 | ||
1255 | void Encoder::initSPS(SPS *sps) | |
1256 | { | |
1257 | m_vps.ptl.progressiveSourceFlag = !m_param->interlaceMode; | |
1258 | m_vps.ptl.interlacedSourceFlag = !!m_param->interlaceMode; | |
1259 | m_vps.ptl.nonPackedConstraintFlag = false; | |
1260 | m_vps.ptl.frameOnlyConstraintFlag = false; | |
1261 | ||
1262 | sps->conformanceWindow = m_conformanceWindow; | |
1263 | sps->chromaFormatIdc = m_param->internalCsp; | |
1264 | sps->picWidthInLumaSamples = m_param->sourceWidth; | |
1265 | sps->picHeightInLumaSamples = m_param->sourceHeight; | |
1266 | sps->numCuInWidth = (m_param->sourceWidth + g_maxCUSize - 1) / g_maxCUSize; | |
1267 | sps->numCuInHeight = (m_param->sourceHeight + g_maxCUSize - 1) / g_maxCUSize; | |
1268 | sps->numCUsInFrame = sps->numCuInWidth * sps->numCuInHeight; | |
1269 | sps->numPartitions = NUM_CU_PARTITIONS; | |
1270 | sps->numPartInCUSize = 1 << g_maxFullDepth; | |
1271 | ||
1272 | sps->log2MinCodingBlockSize = g_maxLog2CUSize - g_maxCUDepth; | |
1273 | sps->log2DiffMaxMinCodingBlockSize = g_maxCUDepth; | |
1274 | ||
1275 | sps->quadtreeTULog2MaxSize = X265_MIN(g_maxLog2CUSize, 5); | |
1276 | sps->quadtreeTULog2MinSize = 2; | |
1277 | sps->quadtreeTUMaxDepthInter = m_param->tuQTMaxInterDepth; | |
1278 | sps->quadtreeTUMaxDepthIntra = m_param->tuQTMaxIntraDepth; | |
1279 | ||
1280 | sps->bUseSAO = m_param->bEnableSAO; | |
1281 | ||
1282 | sps->bUseAMP = m_param->bEnableAMP; | |
1283 | sps->maxAMPDepth = m_param->bEnableAMP ? g_maxCUDepth : 0; | |
1284 | ||
1285 | sps->maxDecPicBuffering = m_vps.maxDecPicBuffering; | |
1286 | sps->numReorderPics = m_vps.numReorderPics; | |
1287 | ||
1288 | sps->bUseStrongIntraSmoothing = m_param->bEnableStrongIntraSmoothing; | |
1289 | sps->bTemporalMVPEnabled = m_param->bEnableTemporalMvp; | |
1290 | ||
1291 | VUI& vui = sps->vuiParameters; | |
1292 | vui.aspectRatioInfoPresentFlag = !!m_param->vui.aspectRatioIdc; | |
1293 | vui.aspectRatioIdc = m_param->vui.aspectRatioIdc; | |
1294 | vui.sarWidth = m_param->vui.sarWidth; | |
1295 | vui.sarHeight = m_param->vui.sarHeight; | |
1296 | ||
1297 | vui.overscanInfoPresentFlag = m_param->vui.bEnableOverscanInfoPresentFlag; | |
1298 | vui.overscanAppropriateFlag = m_param->vui.bEnableOverscanAppropriateFlag; | |
1299 | ||
1300 | vui.videoSignalTypePresentFlag = m_param->vui.bEnableVideoSignalTypePresentFlag; | |
1301 | vui.videoFormat = m_param->vui.videoFormat; | |
1302 | vui.videoFullRangeFlag = m_param->vui.bEnableVideoFullRangeFlag; | |
1303 | ||
1304 | vui.colourDescriptionPresentFlag = m_param->vui.bEnableColorDescriptionPresentFlag; | |
1305 | vui.colourPrimaries = m_param->vui.colorPrimaries; | |
1306 | vui.transferCharacteristics = m_param->vui.transferCharacteristics; | |
1307 | vui.matrixCoefficients = m_param->vui.matrixCoeffs; | |
1308 | ||
1309 | vui.chromaLocInfoPresentFlag = m_param->vui.bEnableChromaLocInfoPresentFlag; | |
1310 | vui.chromaSampleLocTypeTopField = m_param->vui.chromaSampleLocTypeTopField; | |
1311 | vui.chromaSampleLocTypeBottomField = m_param->vui.chromaSampleLocTypeBottomField; | |
1312 | ||
1313 | vui.defaultDisplayWindow.bEnabled = m_param->vui.bEnableDefaultDisplayWindowFlag; | |
1314 | vui.defaultDisplayWindow.rightOffset = m_param->vui.defDispWinRightOffset; | |
1315 | vui.defaultDisplayWindow.topOffset = m_param->vui.defDispWinTopOffset; | |
1316 | vui.defaultDisplayWindow.bottomOffset = m_param->vui.defDispWinBottomOffset; | |
1317 | vui.defaultDisplayWindow.leftOffset = m_param->vui.defDispWinLeftOffset; | |
1318 | ||
1319 | vui.frameFieldInfoPresentFlag = !!m_param->interlaceMode; | |
1320 | vui.fieldSeqFlag = !!m_param->interlaceMode; | |
1321 | ||
1322 | vui.hrdParametersPresentFlag = m_param->bEmitHRDSEI; | |
1323 | ||
1324 | vui.timingInfo.numUnitsInTick = m_param->fpsDenom; | |
1325 | vui.timingInfo.timeScale = m_param->fpsNum; | |
1326 | } | |
1327 | ||
1328 | void Encoder::initPPS(PPS *pps) | |
1329 | { | |
1330 | bool bIsVbv = m_param->rc.vbvBufferSize > 0 && m_param->rc.vbvMaxBitrate > 0; | |
1331 | ||
1332 | if (!m_param->bLossless && (m_param->rc.aqMode || bIsVbv)) | |
1333 | { | |
1334 | pps->bUseDQP = true; | |
1335 | pps->maxCuDQPDepth = 0; /* TODO: make configurable? */ | |
1336 | } | |
1337 | else | |
1338 | { | |
1339 | pps->bUseDQP = false; | |
1340 | pps->maxCuDQPDepth = 0; | |
1341 | } | |
1342 | ||
b53f7c52 JB |
1343 | pps->chromaQpOffset[0] = m_param->cbQpOffset; |
1344 | pps->chromaQpOffset[1] = m_param->crQpOffset; | |
72b9787e JB |
1345 | |
1346 | pps->bConstrainedIntraPred = m_param->bEnableConstrainedIntra; | |
1347 | pps->bUseWeightPred = m_param->bEnableWeightedPred; | |
1348 | pps->bUseWeightedBiPred = m_param->bEnableWeightedBiPred; | |
1349 | pps->bTransquantBypassEnabled = m_param->bCULossless || m_param->bLossless; | |
1350 | pps->bTransformSkipEnabled = m_param->bEnableTransformSkip; | |
1351 | pps->bSignHideEnabled = m_param->bEnableSignHiding; | |
1352 | ||
b53f7c52 | 1353 | pps->bDeblockingFilterControlPresent = !m_param->bEnableLoopFilter || m_param->deblockingFilterBetaOffset || m_param->deblockingFilterTCOffset; |
72b9787e | 1354 | pps->bPicDisableDeblockingFilter = !m_param->bEnableLoopFilter; |
b53f7c52 JB |
1355 | pps->deblockingFilterBetaOffsetDiv2 = m_param->deblockingFilterBetaOffset; |
1356 | pps->deblockingFilterTcOffsetDiv2 = m_param->deblockingFilterTCOffset; | |
72b9787e JB |
1357 | |
1358 | pps->bEntropyCodingSyncEnabled = m_param->bEnableWavefront; | |
1359 | } | |
1360 | ||
1361 | void Encoder::configure(x265_param *p) | |
1362 | { | |
1363 | this->m_param = p; | |
1364 | ||
1365 | if (p->keyframeMax < 0) | |
1366 | { | |
1367 | /* A negative max GOP size indicates the user wants only one I frame at | |
1368 | * the start of the stream. Set an infinite GOP distance and disable | |
1369 | * adaptive I frame placement */ | |
1370 | p->keyframeMax = INT_MAX; | |
1371 | p->scenecutThreshold = 0; | |
1372 | } | |
1373 | else if (p->keyframeMax <= 1) | |
1374 | { | |
1375 | // disable lookahead for all-intra encodes | |
1376 | p->bFrameAdaptive = 0; | |
1377 | p->bframes = 0; | |
1378 | } | |
1379 | if (!p->keyframeMin) | |
1380 | { | |
1381 | double fps = (double)p->fpsNum / p->fpsDenom; | |
1382 | p->keyframeMin = X265_MIN((int)fps, p->keyframeMax / 10); | |
1383 | } | |
1384 | p->keyframeMin = X265_MAX(1, X265_MIN(p->keyframeMin, p->keyframeMax / 2 + 1)); | |
1385 | ||
1386 | if (p->bBPyramid && !p->bframes) | |
1387 | p->bBPyramid = 0; | |
1388 | ||
1389 | /* Disable features which are not supported by the current RD level */ | |
b53f7c52 JB |
1390 | if (p->rdLevel < 5) |
1391 | { | |
1392 | if (p->bEnableCbfFastMode) /* impossible */ | |
1393 | x265_log(p, X265_LOG_WARNING, "--fast-cbf disabled, requires --rdlevel 5 or higher\n"); | |
1394 | p->bEnableCbfFastMode = 0; | |
1395 | } | |
72b9787e JB |
1396 | if (p->rdLevel < 4) |
1397 | { | |
1398 | if (p->psyRdoq > 0) /* impossible */ | |
1399 | x265_log(p, X265_LOG_WARNING, "--psy-rdoq disabled, requires --rdlevel 4 or higher\n"); | |
1400 | p->psyRdoq = 0; | |
1401 | } | |
1402 | if (p->rdLevel < 3) | |
1403 | { | |
1404 | if (p->bCULossless) /* impossible */ | |
1405 | x265_log(p, X265_LOG_WARNING, "--cu-lossless disabled, requires --rdlevel 3 or higher\n"); | |
1406 | if (p->bEnableTransformSkip) /* impossible */ | |
1407 | x265_log(p, X265_LOG_WARNING, "--tskip disabled, requires --rdlevel 3 or higher\n"); | |
1408 | p->bCULossless = p->bEnableTransformSkip = 0; | |
1409 | } | |
1410 | if (p->rdLevel < 2) | |
1411 | { | |
1412 | if (p->bDistributeModeAnalysis) /* not useful */ | |
1413 | x265_log(p, X265_LOG_WARNING, "--pmode disabled, requires --rdlevel 2 or higher\n"); | |
1414 | p->bDistributeModeAnalysis = 0; | |
1415 | ||
1416 | if (p->psyRd > 0) /* impossible */ | |
1417 | x265_log(p, X265_LOG_WARNING, "--psy-rd disabled, requires --rdlevel 2 or higher\n"); | |
1418 | p->psyRd = 0; | |
1419 | ||
1420 | if (p->bEnableRectInter) /* broken, not very useful */ | |
1421 | x265_log(p, X265_LOG_WARNING, "--rect disabled, requires --rdlevel 2 or higher\n"); | |
1422 | p->bEnableRectInter = 0; | |
1423 | } | |
1424 | ||
1425 | if (!p->bEnableRectInter) /* not useful */ | |
1426 | p->bEnableAMP = false; | |
1427 | ||
1428 | /* In 444, chroma gets twice as much resolution, so halve quality when psy-rd is enabled */ | |
1429 | if (p->internalCsp == X265_CSP_I444 && p->psyRd) | |
1430 | { | |
1431 | p->cbQpOffset += 6; | |
1432 | p->crQpOffset += 6; | |
1433 | } | |
1434 | ||
1435 | if (p->bLossless) | |
1436 | { | |
1437 | p->rc.rateControlMode = X265_RC_CQP; | |
1438 | p->rc.qp = 4; // An oddity, QP=4 is more lossless than QP=0 and gives better lambdas | |
1439 | p->bEnableSsim = 0; | |
1440 | p->bEnablePsnr = 0; | |
1441 | } | |
1442 | ||
1443 | if (p->rc.rateControlMode == X265_RC_CQP) | |
1444 | { | |
1445 | p->rc.aqMode = X265_AQ_NONE; | |
1446 | p->rc.bitrate = 0; | |
1447 | p->rc.cuTree = 0; | |
1448 | p->rc.aqStrength = 0; | |
1449 | } | |
1450 | ||
1451 | if (p->rc.aqMode == 0 && p->rc.cuTree) | |
1452 | { | |
1453 | p->rc.aqMode = X265_AQ_VARIANCE; | |
1454 | p->rc.aqStrength = 0.0; | |
1455 | } | |
1456 | ||
1457 | if (p->lookaheadDepth == 0 && p->rc.cuTree && !p->rc.bStatRead) | |
1458 | { | |
1459 | x265_log(p, X265_LOG_WARNING, "cuTree disabled, requires lookahead to be enabled\n"); | |
1460 | p->rc.cuTree = 0; | |
1461 | } | |
1462 | ||
1463 | if (p->rc.aqStrength == 0 && p->rc.cuTree == 0) | |
1464 | p->rc.aqMode = X265_AQ_NONE; | |
1465 | ||
1466 | if (p->rc.aqMode == X265_AQ_NONE && p->rc.cuTree == 0) | |
1467 | p->rc.aqStrength = 0; | |
1468 | ||
1469 | if (p->internalCsp != X265_CSP_I420) | |
1470 | { | |
1471 | x265_log(p, X265_LOG_WARNING, "!! HEVC Range Extension specifications are not finalized !!\n"); | |
1472 | x265_log(p, X265_LOG_WARNING, "!! This output bitstream may not be compliant with the final spec !!\n"); | |
1473 | } | |
1474 | ||
1475 | if (p->scalingLists && p->internalCsp == X265_CSP_I444) | |
1476 | { | |
1477 | x265_log(p, X265_LOG_WARNING, "Scaling lists are not yet supported for 4:4:4 color space\n"); | |
1478 | p->scalingLists = 0; | |
1479 | } | |
1480 | ||
1481 | if (p->interlaceMode) | |
1482 | x265_log(p, X265_LOG_WARNING, "Support for interlaced video is experimental\n"); | |
1483 | ||
1484 | if (p->rc.rfConstantMin > p->rc.rfConstant) | |
1485 | { | |
1486 | x265_log(m_param, X265_LOG_WARNING, "CRF min must be less than CRF\n"); | |
1487 | p->rc.rfConstantMin = 0; | |
1488 | } | |
1489 | ||
1490 | m_bframeDelay = p->bframes ? (p->bBPyramid ? 2 : 1) : 0; | |
1491 | ||
1492 | p->bFrameBias = X265_MIN(X265_MAX(-90, p->bFrameBias), 100); | |
1493 | ||
1494 | if (p->logLevel < X265_LOG_INFO) | |
1495 | { | |
1496 | /* don't measure these metrics if they will not be reported */ | |
1497 | p->bEnablePsnr = 0; | |
1498 | p->bEnableSsim = 0; | |
1499 | } | |
1500 | /* Warn users trying to measure PSNR/SSIM with psy opts on. */ | |
1501 | if (p->bEnablePsnr || p->bEnableSsim) | |
1502 | { | |
1503 | const char *s = NULL; | |
1504 | ||
1505 | if (p->psyRd || p->psyRdoq) | |
1506 | { | |
1507 | s = p->bEnablePsnr ? "psnr" : "ssim"; | |
1508 | x265_log(p, X265_LOG_WARNING, "--%s used with psy on: results will be invalid!\n", s); | |
1509 | } | |
1510 | else if (!p->rc.aqMode && p->bEnableSsim) | |
1511 | { | |
1512 | x265_log(p, X265_LOG_WARNING, "--ssim used with AQ off: results will be invalid!\n"); | |
1513 | s = "ssim"; | |
1514 | } | |
1515 | else if (p->rc.aqStrength > 0 && p->bEnablePsnr) | |
1516 | { | |
1517 | x265_log(p, X265_LOG_WARNING, "--psnr used with AQ on: results will be invalid!\n"); | |
1518 | s = "psnr"; | |
1519 | } | |
1520 | if (s) | |
1521 | x265_log(p, X265_LOG_WARNING, "--tune %s should be used if attempting to benchmark %s!\n", s, s); | |
1522 | } | |
1523 | ||
b53f7c52 | 1524 | /* initialize the conformance window */ |
72b9787e JB |
1525 | m_conformanceWindow.bEnabled = false; |
1526 | m_conformanceWindow.rightOffset = 0; | |
1527 | m_conformanceWindow.topOffset = 0; | |
1528 | m_conformanceWindow.bottomOffset = 0; | |
1529 | m_conformanceWindow.leftOffset = 0; | |
1530 | ||
b53f7c52 JB |
1531 | /* set pad size if width is not multiple of the minimum CU size */ |
1532 | if (p->sourceWidth & (MIN_CU_SIZE - 1)) | |
72b9787e | 1533 | { |
b53f7c52 JB |
1534 | uint32_t rem = p->sourceWidth & (MIN_CU_SIZE - 1); |
1535 | uint32_t padsize = MIN_CU_SIZE - rem; | |
72b9787e JB |
1536 | p->sourceWidth += padsize; |
1537 | ||
72b9787e JB |
1538 | m_conformanceWindow.bEnabled = true; |
1539 | m_conformanceWindow.rightOffset = padsize; | |
1540 | } | |
1541 | ||
b53f7c52 JB |
1542 | /* set pad size if height is not multiple of the minimum CU size */ |
1543 | if (p->sourceHeight & (MIN_CU_SIZE - 1)) | |
72b9787e | 1544 | { |
b53f7c52 JB |
1545 | uint32_t rem = p->sourceHeight & (MIN_CU_SIZE - 1); |
1546 | uint32_t padsize = MIN_CU_SIZE - rem; | |
72b9787e JB |
1547 | p->sourceHeight += padsize; |
1548 | ||
72b9787e JB |
1549 | m_conformanceWindow.bEnabled = true; |
1550 | m_conformanceWindow.bottomOffset = padsize; | |
1551 | } | |
b53f7c52 JB |
1552 | if (p->bDistributeModeAnalysis && p->analysisMode) |
1553 | { | |
1554 | p->analysisMode = X265_ANALYSIS_OFF; | |
1555 | x265_log(p, X265_LOG_WARNING, "Analysis save and load mode not supported for distributed mode analysis\n"); | |
1556 | } | |
1557 | } | |
1558 | ||
1559 | void Encoder::allocAnalysis(x265_analysis_data* analysis) | |
1560 | { | |
1561 | if (analysis->sliceType == X265_TYPE_IDR || analysis->sliceType == X265_TYPE_I) | |
1562 | { | |
1563 | analysis_intra_data *intraData = (analysis_intra_data*)analysis->intraData; | |
1564 | CHECKED_MALLOC_ZERO(intraData, analysis_intra_data, 1); | |
1565 | CHECKED_MALLOC(intraData->depth, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); | |
1566 | CHECKED_MALLOC(intraData->modes, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); | |
1567 | CHECKED_MALLOC(intraData->partSizes, char, analysis->numPartitions * analysis->numCUsInFrame); | |
1568 | analysis->intraData = intraData; | |
1569 | } | |
1570 | else | |
1571 | { | |
1572 | analysis_inter_data *interData = (analysis_inter_data*)analysis->interData; | |
1573 | CHECKED_MALLOC(interData, analysis_inter_data, analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * 2); | |
1574 | analysis->interData = interData; | |
1575 | } | |
1576 | return; | |
1577 | ||
1578 | fail: | |
1579 | freeAnalysis(analysis); | |
1580 | m_aborted = true; | |
1581 | } | |
1582 | ||
1583 | void Encoder::freeAnalysis(x265_analysis_data* analysis) | |
1584 | { | |
1585 | if (analysis->sliceType == X265_TYPE_IDR || analysis->sliceType == X265_TYPE_I) | |
1586 | { | |
1587 | X265_FREE(((analysis_intra_data*)analysis->intraData)->depth); | |
1588 | X265_FREE(((analysis_intra_data*)analysis->intraData)->modes); | |
1589 | X265_FREE(((analysis_intra_data*)analysis->intraData)->partSizes); | |
1590 | X265_FREE(analysis->intraData); | |
1591 | } | |
1592 | else | |
1593 | X265_FREE(analysis->interData); | |
1594 | } | |
1595 | ||
1596 | void Encoder::readAnalysisFile(x265_analysis_data* analysis, int curPoc) | |
1597 | { | |
1598 | ||
1599 | #define X265_FREAD(val, size, readSize, fileOffset)\ | |
1600 | if (fread(val, size, readSize, fileOffset) != readSize)\ | |
1601 | {\ | |
1602 | x265_log(NULL, X265_LOG_ERROR, "Error reading analysis data\n");\ | |
1603 | freeAnalysis(analysis);\ | |
1604 | m_aborted = true;\ | |
1605 | return;\ | |
1606 | }\ | |
1607 | ||
1608 | static uint64_t consumedBytes = 0; | |
1609 | static uint64_t totalConsumedBytes = 0; | |
1610 | fseeko(m_analysisFile, totalConsumedBytes, SEEK_SET); | |
1611 | ||
1612 | int poc; uint32_t frameRecordSize; | |
1613 | X265_FREAD(&frameRecordSize, sizeof(uint32_t), 1, m_analysisFile); | |
1614 | X265_FREAD(&poc, sizeof(int), 1, m_analysisFile); | |
1615 | ||
1616 | uint64_t currentOffset = totalConsumedBytes; | |
1617 | ||
1618 | /* Seeking to the right frame Record */ | |
1619 | while (poc != curPoc && !feof(m_analysisFile)) | |
1620 | { | |
1621 | currentOffset += frameRecordSize; | |
1622 | fseeko(m_analysisFile, currentOffset, SEEK_SET); | |
1623 | X265_FREAD(&frameRecordSize, sizeof(uint32_t), 1, m_analysisFile); | |
1624 | X265_FREAD(&poc, sizeof(int), 1, m_analysisFile); | |
1625 | } | |
1626 | ||
1627 | if (poc != curPoc || feof(m_analysisFile)) | |
1628 | { | |
1629 | x265_log(NULL, X265_LOG_WARNING, "Error reading analysis data: Cannot find POC %d\n", curPoc); | |
1630 | freeAnalysis(analysis); | |
1631 | return; | |
1632 | } | |
1633 | ||
1634 | /* Now arrived at the right frame, read the record */ | |
1635 | analysis->poc = poc; | |
1636 | analysis->frameRecordSize = frameRecordSize; | |
1637 | X265_FREAD(&analysis->sliceType, sizeof(int), 1, m_analysisFile); | |
1638 | X265_FREAD(&analysis->numCUsInFrame, sizeof(int), 1, m_analysisFile); | |
1639 | X265_FREAD(&analysis->numPartitions, sizeof(int), 1, m_analysisFile); | |
1640 | ||
1641 | /* Memory is allocated for inter and intra analysis data based on the slicetype */ | |
1642 | allocAnalysis(analysis); | |
1643 | ||
1644 | if (analysis->sliceType == X265_TYPE_IDR || analysis->sliceType == X265_TYPE_I) | |
1645 | { | |
1646 | X265_FREAD(((analysis_intra_data *)analysis->intraData)->depth, sizeof(uint8_t), analysis->numCUsInFrame * analysis->numPartitions, m_analysisFile); | |
1647 | X265_FREAD(((analysis_intra_data *)analysis->intraData)->modes, sizeof(uint8_t), analysis->numCUsInFrame * analysis->numPartitions, m_analysisFile); | |
1648 | X265_FREAD(((analysis_intra_data *)analysis->intraData)->partSizes, sizeof(char), analysis->numCUsInFrame * analysis->numPartitions, m_analysisFile); | |
1649 | analysis->sliceType = X265_TYPE_I; | |
1650 | consumedBytes += frameRecordSize; | |
1651 | } | |
1652 | else if (analysis->sliceType == X265_TYPE_P) | |
1653 | { | |
1654 | X265_FREAD(analysis->interData, sizeof(analysis_inter_data), analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU, m_analysisFile); | |
1655 | consumedBytes += frameRecordSize; | |
1656 | totalConsumedBytes = consumedBytes; | |
1657 | } | |
1658 | else | |
1659 | { | |
1660 | X265_FREAD(analysis->interData, sizeof(analysis_inter_data), analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * 2, m_analysisFile); | |
1661 | consumedBytes += frameRecordSize; | |
1662 | } | |
1663 | #undef X265_FREAD | |
1664 | } | |
1665 | ||
1666 | void Encoder::writeAnalysisFile(x265_analysis_data* analysis) | |
1667 | { | |
1668 | ||
1669 | #define X265_FWRITE(val, size, writeSize, fileOffset)\ | |
1670 | if (fwrite(val, size, writeSize, fileOffset) < writeSize)\ | |
1671 | {\ | |
1672 | x265_log(NULL, X265_LOG_ERROR, "Error writing analysis data\n");\ | |
1673 | freeAnalysis(analysis);\ | |
1674 | m_aborted = true;\ | |
1675 | return;\ | |
1676 | }\ | |
1677 | ||
1678 | /* calculate frameRecordSize */ | |
1679 | analysis->frameRecordSize = sizeof(analysis->frameRecordSize) + sizeof(analysis->poc) + sizeof(analysis->sliceType) + | |
1680 | sizeof(analysis->numCUsInFrame) + sizeof(analysis->numPartitions); | |
1681 | if (analysis->sliceType == X265_TYPE_IDR || analysis->sliceType == X265_TYPE_I) | |
1682 | analysis->frameRecordSize += sizeof(uint8_t) * analysis->numCUsInFrame * analysis->numPartitions * 3; | |
1683 | else if (analysis->sliceType == X265_TYPE_P) | |
1684 | analysis->frameRecordSize += sizeof(analysis_inter_data) * analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU; | |
1685 | else | |
1686 | analysis->frameRecordSize += sizeof(analysis_inter_data) * analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * 2; | |
1687 | ||
1688 | X265_FWRITE(&analysis->frameRecordSize, sizeof(uint32_t), 1, m_analysisFile); | |
1689 | X265_FWRITE(&analysis->poc, sizeof(int), 1, m_analysisFile); | |
1690 | X265_FWRITE(&analysis->sliceType, sizeof(int), 1, m_analysisFile); | |
1691 | X265_FWRITE(&analysis->numCUsInFrame, sizeof(int), 1, m_analysisFile); | |
1692 | X265_FWRITE(&analysis->numPartitions, sizeof(int), 1, m_analysisFile); | |
1693 | ||
1694 | if (analysis->sliceType == X265_TYPE_IDR || analysis->sliceType == X265_TYPE_I) | |
1695 | { | |
1696 | X265_FWRITE(((analysis_intra_data*)analysis->intraData)->depth, sizeof(uint8_t), analysis->numCUsInFrame * analysis->numPartitions, m_analysisFile); | |
1697 | X265_FWRITE(((analysis_intra_data*)analysis->intraData)->modes, sizeof(uint8_t), analysis->numCUsInFrame * analysis->numPartitions, m_analysisFile); | |
1698 | X265_FWRITE(((analysis_intra_data*)analysis->intraData)->partSizes, sizeof(char), analysis->numCUsInFrame * analysis->numPartitions, m_analysisFile); | |
1699 | } | |
1700 | else if (analysis->sliceType == X265_TYPE_P) | |
1701 | { | |
1702 | X265_FWRITE(analysis->interData, sizeof(analysis_inter_data), analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU, m_analysisFile); | |
1703 | } | |
1704 | else | |
1705 | { | |
1706 | X265_FWRITE(analysis->interData, sizeof(analysis_inter_data), analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * 2, m_analysisFile); | |
1707 | } | |
1708 | #undef X265_FWRITE | |
72b9787e | 1709 | } |