1 /*****************************************************************************
2 * Copyright (C) 2013 x265 project
4 * Authors: Steve Borho <steve@borho.org>
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.
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.
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.
20 * This program is also available under a commercial proprietary license.
21 * For more information, contact us at license @ x265.com.
22 *****************************************************************************/
25 #include "primitives.h"
26 #include "threadpool.h"
29 #include "framedata.h"
34 #include "slicetype.h"
35 #include "frameencoder.h"
36 #include "ratecontrol.h"
43 const char g_sliceTypeToChar
[] = {'B', 'P', 'I'};
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), "
54 const char* defaultAnalysisFileName
= "x265_analysis.dat";
61 m_encodedFrameNum
= 0;
64 m_numLumaWPFrames
= 0;
65 m_numChromaWPFrames
= 0;
66 m_numLumaWPBiFrames
= 0;
67 m_numChromaWPBiFrames
= 0;
69 m_frameEncoder
= NULL
;
82 m_numThreadLocalData
= 0;
83 m_analysisFile
= NULL
;
86 void Encoder::create()
88 if (!primitives
.sad
[0])
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");
95 x265_param
* p
= m_param
;
97 int rows
= (p
->sourceHeight
+ p
->maxCUSize
- 1) >> g_log2Size
[p
->maxCUSize
];
99 // Do not allow WPP if only one row, it is pointless and unstable
101 p
->bEnableWavefront
= 0;
103 int poolThreadCount
= p
->poolNumThreads
? p
->poolNumThreads
: getCpuCount();
105 // Trim the thread pool if --wpp, --pme, and --pmode are disabled
106 if (!p
->bEnableWavefront
&& !p
->bDistributeModeAnalysis
&& !p
->bDistributeMotionEstimation
)
109 if (poolThreadCount
> 1)
111 m_threadPool
= ThreadPool::allocThreadPool(poolThreadCount
);
112 poolThreadCount
= m_threadPool
->getThreadCount();
117 if (!poolThreadCount
)
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");
127 // disable all pool features if the thread pool is disabled or unusable.
128 p
->bEnableWavefront
= p
->bDistributeModeAnalysis
= p
->bDistributeMotionEstimation
= 0;
131 if (!p
->frameNumThreads
)
133 // auto-detect frame threads
134 int cpuCount
= getCpuCount();
135 if (!p
->bEnableWavefront
)
136 p
->frameNumThreads
= X265_MIN(cpuCount
, (rows
+ 1) / 2);
137 else if (cpuCount
>= 32)
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
146 p
->frameNumThreads
= 1;
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" : "");
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
);
157 if (!m_scalingList
.init())
159 x265_log(m_param
, X265_LOG_ERROR
, "Unable to allocate scaling list arrays\n");
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
))
168 m_scalingList
.setupQuantMatrices();
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
++)
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
);
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
];
187 m_lookahead
= new Lookahead(m_param
, m_threadPool
);
188 m_dpb
= new DPB(m_param
);
189 m_rateControl
= new RateControl(m_param
);
194 /* Try to open CSV file handle */
197 m_csvfpt
= fopen(m_param
->csvfn
, "r");
200 /* file already exists, re-open for append */
202 m_csvfpt
= fopen(m_param
->csvfn
, "ab");
206 /* new CSV file, write header */
207 m_csvfpt
= fopen(m_param
->csvfn
, "wb");
210 if (m_param
->logLevel
>= X265_LOG_DEBUG
)
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");
219 fputs(summaryCSVHeader
, m_csvfpt
);
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
++)
230 if (!m_frameEncoder
[i
].init(this, numRows
, numCols
, i
))
232 x265_log(m_param
, X265_LOG_ERROR
, "Unable to initialize frame encoder, aborting\n");
238 if (m_param
->bEmitHRDSEI
)
239 m_rateControl
->initHRD(&m_sps
);
240 if (!m_rateControl
->init(&m_sps
))
245 if (m_param
->analysisMode
)
247 const char* name
= m_param
->analysisFileName
;
249 name
= defaultAnalysisFileName
;
250 const char* mode
= m_param
->analysisMode
== X265_ANALYSIS_LOAD
? "rb" : "wb";
251 m_analysisFile
= fopen(name
, mode
);
254 x265_log(NULL
, X265_LOG_ERROR
, "Analysis load/save: failed to open file %s\n", name
);
259 m_aborted
|= parseLambdaFile(m_param
);
261 m_encodeStartTime
= x265_mdate();
264 void Encoder::destroy()
268 ATOMIC_DEC(&m_exportedPic
->m_countRefEncoders
);
269 m_exportedPic
= NULL
;
273 m_rateControl
->terminate(); // unblock all blocked RC calls
277 for (int i
= 0; i
< m_param
->frameNumThreads
; i
++)
279 // Ensure frame encoder is idle before destroying it
280 m_frameEncoder
[i
].getEncodedPicture(m_nalList
);
281 m_frameEncoder
[i
].destroy();
284 delete [] m_frameEncoder
;
287 for (int i
= 0; i
< m_numThreadLocalData
; i
++)
288 m_threadLocalData
[i
].destroy();
290 delete [] m_threadLocalData
;
294 m_lookahead
->destroy();
301 m_rateControl
->destroy();
302 delete m_rateControl
;
304 // thread pool release should always happen last
306 m_threadPool
->release();
308 X265_FREE(m_cuOffsetY
);
309 X265_FREE(m_cuOffsetC
);
310 X265_FREE(m_buOffsetY
);
311 X265_FREE(m_buOffsetC
);
314 fclose(m_analysisFile
);
315 free(m_param
->analysisFileName
);
316 free(m_param
->csvfn
);
319 free(m_param
->rc
.statFileName
); // alloc'd by strdup
324 void Encoder::updateVbvPlan(RateControl
* rc
)
326 for (int i
= 0; i
< m_param
->frameNumThreads
; i
++)
328 FrameEncoder
*encoder
= &m_frameEncoder
[i
];
329 if (encoder
->m_rce
.isActive
&& encoder
->m_rce
.poc
!= rc
->m_curSlice
->m_poc
)
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
);
337 rc
->m_predictedBits
+= bits
;
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.
347 * pic_in input original YUV picture or NULL
348 * pic_out pointer to reconstructed picture struct
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
)
360 ATOMIC_DEC(&m_exportedPic
->m_countRefEncoders
);
361 m_exportedPic
= NULL
;
362 m_dpb
->recycleUnreferenced();
367 if (pic_in
->colorSpace
!= m_param
->internalCsp
)
369 x265_log(m_param
, X265_LOG_ERROR
, "Unsupported color space (%d) on input\n",
373 if (pic_in
->bitDepth
< 8 || pic_in
->bitDepth
> 16)
375 x265_log(m_param
, X265_LOG_ERROR
, "Input bit depth (%d) must be between 8 and 16\n",
381 if (m_dpb
->m_freeList
.empty())
384 if (inFrame
->create(m_param
))
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 */
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
;
398 if (!inFrame
->m_fencPic
->createOffsets(m_sps
))
401 x265_log(m_param
, X265_LOG_ERROR
, "memory allocation failure, aborting encode\n");
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
;
418 x265_log(m_param
, X265_LOG_ERROR
, "memory allocation failure, aborting encode\n");
425 inFrame
= m_dpb
->m_freeList
.popBack();
427 /* Copy input picture into a Frame and PicYuv, send to lookahead */
428 inFrame
->m_poc
= ++m_pocLast
;
429 inFrame
->m_fencPic
->copyFromPicture(*pic_in
, m_sps
.conformanceWindow
.rightOffset
, m_sps
.conformanceWindow
.bottomOffset
);
431 inFrame
->m_userData
= pic_in
->userData
;
432 inFrame
->m_pts
= pic_in
->pts
;
433 inFrame
->m_forceqp
= pic_in
->forceqp
;
436 m_firstPts
= inFrame
->m_pts
;
437 if (m_bframeDelay
&& m_pocLast
== m_bframeDelay
)
438 m_bframeDelayTime
= inFrame
->m_pts
- m_firstPts
;
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
)
445 if (m_param
->rc
.cuTree
&& m_param
->rc
.bStatRead
)
447 if (!m_rateControl
->cuTreeReadFor2Pass(inFrame
))
454 m_rateControl
->calcAdaptiveQuantFrame(inFrame
);
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
;
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
)
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
;
476 m_lookahead
->addPicture(inFrame
, sliceType
);
480 m_lookahead
->flush();
482 FrameEncoder
*curEncoder
= &m_frameEncoder
[m_curEncoder
];
483 m_curEncoder
= (m_curEncoder
+ 1) % m_param
->frameNumThreads
;
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
);
493 Slice
*slice
= outFrame
->m_encData
->m_slice
;
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
);
501 PicYuv
*recpic
= outFrame
->m_reconPic
;
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
;
507 pic_out
->pts
= outFrame
->m_pts
;
508 pic_out
->dts
= outFrame
->m_dts
;
510 switch (slice
->m_sliceType
)
513 pic_out
->sliceType
= outFrame
->m_lowres
.bKeyframe
? X265_TYPE_IDR
: X265_TYPE_I
;
516 pic_out
->sliceType
= X265_TYPE_P
;
519 pic_out
->sliceType
= X265_TYPE_B
;
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
));
530 /* Dump analysis data from pic_out to file in save mode and free */
531 if (m_param
->analysisMode
== X265_ANALYSIS_SAVE
)
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
);
543 if (slice
->m_sliceType
== P_SLICE
)
545 if (slice
->m_weightPredTable
[0][0][0].bPresentFlag
)
547 if (slice
->m_weightPredTable
[0][0][1].bPresentFlag
||
548 slice
->m_weightPredTable
[0][0][2].bPresentFlag
)
549 m_numChromaWPFrames
++;
551 else if (slice
->m_sliceType
== B_SLICE
)
553 bool bLuma
= false, bChroma
= false;
554 for (int l
= 0; l
< 2; l
++)
556 if (slice
->m_weightPredTable
[l
][0][0].bPresentFlag
)
558 if (slice
->m_weightPredTable
[l
][0][1].bPresentFlag
||
559 slice
->m_weightPredTable
[l
][0][2].bPresentFlag
)
564 m_numLumaWPBiFrames
++;
566 m_numChromaWPBiFrames
++;
570 finishFrameStats(outFrame
, curEncoder
, curEncoder
->m_accessUnitBits
);
572 // Allow this frame to be recycled if no frame encoders are using it for reference
575 ATOMIC_DEC(&outFrame
->m_countRefEncoders
);
576 m_dpb
->recycleUnreferenced();
579 m_exportedPic
= outFrame
;
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();
591 // give this picture a FrameData instance before encoding
592 if (m_dpb
->m_picSymFreeList
)
594 frameEnc
->m_encData
= m_dpb
->m_picSymFreeList
;
595 m_dpb
->m_picSymFreeList
= m_dpb
->m_picSymFreeList
->m_freeListNext
;
596 frameEnc
->reinit(m_sps
);
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
);
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
;
611 curEncoder
->m_rce
.encodeOrder
= m_encodedFrameNum
++;
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
;
621 frameEnc
->m_dts
= frameEnc
->m_reorderedPts
;
622 /* Allocate analysis data before encode in save mode. This is allocated in frameEnc*/
623 if (m_param
->analysisMode
== X265_ANALYSIS_SAVE
)
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
;
631 uint32_t numCUsInFrame
= widthInCU
* heightInCU
;
632 analysis
->numCUsInFrame
= numCUsInFrame
;
633 analysis
->numPartitions
= NUM_CU_PARTITIONS
;
634 allocAnalysis(analysis
);
637 // determine references, setup RPS, etc
638 m_dpb
->prepareEncode(frameEnc
);
640 if (m_param
->rc
.rateControlMode
!= X265_RC_CQP
)
641 m_lookahead
->getEstimatedPictureCost(frameEnc
);
643 // Allow FrameEncoder::compressFrame() to start in the frame encoder thread
644 if (!curEncoder
->startCompressFrame(frameEnc
))
647 else if (m_encodedFrameNum
)
648 m_rateControl
->setFinalFrameCount(m_encodedFrameNum
);
653 void EncStats::addPsnr(double psnrY
, double psnrU
, double psnrV
)
660 void EncStats::addBits(uint64_t bits
)
666 void EncStats::addSsim(double ssim
)
668 m_globalSsim
+= ssim
;
671 void EncStats::addQP(double aveQp
)
676 char* Encoder::statsCSVString(EncStats
& stat
, char* buffer
)
680 sprintf(buffer
, "-, -, -, -, -, -, -, ");
684 double fps
= (double)m_param
->fpsNum
/ m_param
->fpsDenom
;
685 double scale
= fps
/ 1000 / (double)stat
.m_numPics
;
687 int len
= sprintf(buffer
, "%-6u, ", stat
.m_numPics
);
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
)
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
);
699 len
+= sprintf(buffer
+ len
, "-, -, -, ");
701 if (m_param
->bEnableSsim
)
702 sprintf(buffer
+ len
, "%.3lf, ", x265_ssim2dB(stat
.m_globalSsim
/ (double)stat
.m_numPics
));
704 sprintf(buffer
+ len
, "-, ");
708 char* Encoder::statsString(EncStats
& stat
, char* buffer
)
710 double fps
= (double)m_param
->fpsNum
/ m_param
->fpsDenom
;
711 double scale
= fps
/ 1000 / (double)stat
.m_numPics
;
713 int len
= sprintf(buffer
, "%6u, ", stat
.m_numPics
);
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
)
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
);
724 if (m_param
->bEnableSsim
)
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
));
733 void Encoder::printSummary()
735 if (m_param
->logLevel
< X265_LOG_INFO
)
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
)
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
);
753 if (m_param
->bEnableWeightedBiPred
&& m_analyzeB
.m_numPics
)
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
);
760 for (int i
= 0; i
<= m_param
->bframes
; i
++)
761 pWithB
+= m_lookahead
->m_histogram
[i
];
766 for (int i
= 0; i
<= m_param
->bframes
; i
++)
767 p
+= sprintf(buffer
+ p
, "%.1f%% ", 100. * m_lookahead
->m_histogram
[i
] / pWithB
);
769 x265_log(m_param
, X265_LOG_INFO
, "consecutive B-frames: %s\n", buffer
);
771 if (m_param
->bLossless
)
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
;
777 x265_log(m_param
, X265_LOG_INFO
, "lossless compression ratio %.2f::1\n", uncompressed
/ m_analyzeAll
.m_accBits
);
780 if (!m_param
->bLogCuStats
)
783 for (int sliceType
= 2; sliceType
>= 0; sliceType
--)
785 if (sliceType
== P_SLICE
&& !m_analyzeP
.m_numPics
)
787 if (sliceType
== B_SLICE
&& !m_analyzeB
.m_numPics
)
790 StatisticLog finalLog
;
791 for (uint32_t depth
= 0; depth
<= g_maxCUDepth
; depth
++)
793 for (int i
= 0; i
< m_param
->frameNumThreads
; i
++)
795 StatisticLog
& enclog
= m_frameEncoder
[i
].m_sliceTypeLog
[sliceType
];
797 finalLog
.totalCu
+= enclog
.totalCu
;
798 finalLog
.cntIntra
[depth
] += enclog
.cntIntra
[depth
];
799 for (int m
= 0; m
< INTER_MODES
; m
++)
802 finalLog
.cuIntraDistribution
[depth
][m
] += enclog
.cuIntraDistribution
[depth
][m
];
803 finalLog
.cuInterDistribution
[depth
][m
] += enclog
.cuInterDistribution
[depth
][m
];
806 if (depth
== g_maxCUDepth
)
807 finalLog
.cntIntraNxN
+= enclog
.cntIntraNxN
;
808 if (sliceType
!= I_SLICE
)
810 finalLog
.cntTotalCu
[depth
] += enclog
.cntTotalCu
[depth
];
811 finalLog
.cntInter
[depth
] += enclog
.cntInter
[depth
];
812 finalLog
.cntSkipCu
[depth
] += enclog
.cntSkipCu
[depth
];
816 uint64_t cntInter
, cntSkipCu
, cntIntra
= 0, cntIntraNxN
= 0, encCu
= 0;
817 uint64_t cuInterDistribution
[INTER_MODES
], cuIntraDistribution
[INTRA_MODES
];
819 // check for 0/0, if true assign 0 else calculate percentage
820 for (int n
= 0; n
< INTER_MODES
; n
++)
822 if (!finalLog
.cntInter
[depth
])
823 cuInterDistribution
[n
] = 0;
825 cuInterDistribution
[n
] = (finalLog
.cuInterDistribution
[depth
][n
] * 100) / finalLog
.cntInter
[depth
];
829 if (!finalLog
.cntIntra
[depth
])
832 cuIntraDistribution
[n
] = 0;
836 cntIntraNxN
= (finalLog
.cntIntraNxN
* 100) / finalLog
.cntIntra
[depth
];
837 cuIntraDistribution
[n
] = (finalLog
.cuIntraDistribution
[depth
][n
] * 100) / finalLog
.cntIntra
[depth
];
842 if (!finalLog
.totalCu
)
844 else if (sliceType
== I_SLICE
)
846 cntIntra
= (finalLog
.cntIntra
[depth
] * 100) / finalLog
.totalCu
;
847 cntIntraNxN
= (finalLog
.cntIntraNxN
* 100) / finalLog
.totalCu
;
850 encCu
= ((finalLog
.cntIntra
[depth
] + finalLog
.cntInter
[depth
]) * 100) / finalLog
.totalCu
;
852 if (sliceType
== I_SLICE
)
857 else if (!finalLog
.cntTotalCu
[depth
])
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
];
871 int cuSize
= g_maxCUSize
>> depth
;
872 char stats
[256] = { 0 };
874 if (sliceType
!= I_SLICE
)
875 len
+= sprintf(stats
+ len
, " EncCU "X265_LL
"%% Merge "X265_LL
"%%", encCu
, cntSkipCu
);
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]);
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
)
899 if (depth
== g_maxCUDepth
)
900 len
+= sprintf(stats
+ len
, " %dx%d "X265_LL
"%%", cuSize
/ 2, cuSize
/ 2, cntIntraNxN
);
903 len
+= sprintf(stats
+ len
, ")");
904 if (sliceType
== I_SLICE
)
906 if (depth
== g_maxCUDepth
)
907 len
+= sprintf(stats
+ len
, " %dx%d: "X265_LL
"%%", cuSize
/ 2, cuSize
/ 2, cntIntraNxN
);
910 const char slicechars
[] = "BPI";
912 x265_log(m_param
, X265_LOG_INFO
, "%c%-2d:%s\n", slicechars
[sliceType
], cuSize
, stats
);
917 void Encoder::fetchStats(x265_stats
*stats
, size_t statsSizeBytes
)
919 if (statsSizeBytes
>= sizeof(stats
))
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)
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
;
937 stats
->globalSsim
= 0;
938 stats
->globalPsnr
= 0;
940 stats
->elapsedVideoTime
= 0;
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
949 void Encoder::writeLog(int argc
, char **argv
)
953 if (m_param
->logLevel
>= X265_LOG_DEBUG
)
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
);
959 // CLI arguments or other
960 for (int i
= 1; i
< argc
; i
++)
962 if (i
) fputc(' ', m_csvfpt
);
963 fputs(argv
[i
], m_csvfpt
);
966 // current date and time
970 timeinfo
= localtime(&now
);
972 strftime(buffer
, 128, "%c", timeinfo
);
973 fprintf(m_csvfpt
, ", %s, ", buffer
);
976 fetchStats(&stats
, sizeof(stats
));
978 // elapsed time, fps, bitrate
979 fprintf(m_csvfpt
, "%.2f, %.2f, %.2f,",
980 stats
.elapsedEncodeTime
, stats
.encodedPictureCount
/ stats
.elapsedEncodeTime
, stats
.bitrate
);
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
);
987 fprintf(m_csvfpt
, " -, -, -, -,");
988 if (m_param
->bEnableSsim
)
989 fprintf(m_csvfpt
, " %.6f, %6.3f,", stats
.globalSsim
, x265_ssim2dB(stats
.globalSsim
));
991 fprintf(m_csvfpt
, " -, -,");
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
);
1001 * Produce an ascii(hex) representation of picture digest.
1003 * Returns: a statically allocated null-terminated string. DO NOT FREE.
1005 static const char*digestToString(const unsigned char digest
[3][16], int numChar
)
1007 const char* hex
= "0123456789abcdef";
1008 static char string
[99];
1011 for (int yuvIdx
= 0; yuvIdx
< 3; yuvIdx
++)
1013 for (int i
= 0; i
< numChar
; i
++)
1015 string
[cnt
++] = hex
[digest
[yuvIdx
][i
] >> 4];
1016 string
[cnt
++] = hex
[digest
[yuvIdx
][i
] & 0xf];
1019 string
[cnt
++] = ',';
1022 string
[cnt
- 1] = '\0';
1026 void Encoder::finishFrameStats(Frame
* curFrame
, FrameEncoder
*curEncoder
, uint64_t bits
)
1028 PicYuv
* reconPic
= curFrame
->m_reconPic
;
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
;
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
;
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);
1048 FrameData
& curEncData
= *curFrame
->m_encData
;
1049 Slice
* slice
= curEncData
.m_slice
;
1051 //===== add bits, psnr and ssim =====
1052 m_analyzeAll
.addBits(bits
);
1053 m_analyzeAll
.addQP(curEncData
.m_avgQpAq
);
1055 if (m_param
->bEnablePsnr
)
1056 m_analyzeAll
.addPsnr(psnrY
, psnrU
, psnrV
);
1059 if (m_param
->bEnableSsim
&& curEncoder
->m_ssimCnt
)
1061 ssim
= curEncoder
->m_ssim
/ curEncoder
->m_ssimCnt
;
1062 m_analyzeAll
.addSsim(ssim
);
1064 if (slice
->isIntra())
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
);
1073 else if (slice
->isInterP())
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
);
1082 else if (slice
->isInterB())
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
);
1092 // if debug log level is enabled, per frame logging is performed
1093 if (m_param
->logLevel
>= X265_LOG_DEBUG
)
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
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
));
1110 if (!slice
->isIntra())
1112 int numLists
= slice
->isInterP() ? 1 : 2;
1113 for (int list
= 0; list
< numLists
; list
++)
1115 p
+= sprintf(buf
+ p
, " [L%d ", list
);
1116 for (int ref
= 0; ref
< slice
->m_numRefIdx
[list
]; ref
++)
1118 int k
= slice
->m_refPOCList
[list
][ref
] - slice
->m_lastIDR
;
1119 p
+= sprintf(buf
+ p
, "%d ", k
);
1122 p
+= sprintf(buf
+ p
, "]");
1126 // per frame CSV logging if the file handle is valid
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
);
1136 fprintf(m_csvfpt
, " -, -, -, -,");
1137 if (m_param
->bEnableSsim
)
1138 fprintf(m_csvfpt
, " %.6f, %6.3f,", ssim
, x265_ssim2dB(ssim
));
1140 fprintf(m_csvfpt
, " -, -,");
1141 fprintf(m_csvfpt
, " %.3lf, %.3lf", curEncoder
->m_frameTime
, curEncoder
->m_elapsedCompressTime
);
1142 if (!slice
->isIntra())
1144 int numLists
= slice
->isInterP() ? 1 : 2;
1145 for (int list
= 0; list
< numLists
; list
++)
1147 fprintf(m_csvfpt
, ", ");
1148 for (int ref
= 0; ref
< slice
->m_numRefIdx
[list
]; ref
++)
1150 int k
= slice
->m_refPOCList
[list
][ref
] - slice
->m_lastIDR
;
1151 fprintf(m_csvfpt
, " %d", k
);
1156 fprintf(m_csvfpt
, ", -");
1159 fprintf(m_csvfpt
, ", -, -");
1160 fprintf(m_csvfpt
, "\n");
1163 if (m_param
->decodedPictureHashSEI
&& m_param
->logLevel
>= X265_LOG_FULL
)
1165 const char* digestStr
= NULL
;
1166 if (m_param
->decodedPictureHashSEI
== 1)
1168 digestStr
= digestToString(curEncoder
->m_seiReconPictureDigest
.m_digest
, 16);
1169 p
+= sprintf(buf
+ p
, " [MD5:%s]", digestStr
);
1171 else if (m_param
->decodedPictureHashSEI
== 2)
1173 digestStr
= digestToString(curEncoder
->m_seiReconPictureDigest
.m_digest
, 2);
1174 p
+= sprintf(buf
+ p
, " [CRC:%s]", digestStr
);
1176 else if (m_param
->decodedPictureHashSEI
== 3)
1178 digestStr
= digestToString(curEncoder
->m_seiReconPictureDigest
.m_digest
, 4);
1179 p
+= sprintf(buf
+ p
, " [Checksum:%s]", digestStr
);
1182 x265_log(m_param
, X265_LOG_DEBUG
, "%s\n", buf
);
1187 #if defined(_MSC_VER)
1188 #pragma warning(disable: 4800) // forcing int to bool
1189 #pragma warning(disable: 4127) // conditional expression is constant
1192 void Encoder::getStreamHeaders(NALList
& list
, Entropy
& sbacCoder
, Bitstream
& bs
)
1194 sbacCoder
.setBitstream(&bs
);
1196 /* headers for start of bitstream */
1198 sbacCoder
.codeVPS(m_vps
);
1199 bs
.writeByteAlignment();
1200 list
.serialize(NAL_UNIT_VPS
, bs
);
1203 sbacCoder
.codeSPS(m_sps
, m_scalingList
, m_vps
.ptl
);
1204 bs
.writeByteAlignment();
1205 list
.serialize(NAL_UNIT_SPS
, bs
);
1208 sbacCoder
.codePPS(m_pps
);
1209 bs
.writeByteAlignment();
1210 list
.serialize(NAL_UNIT_PPS
, bs
);
1212 if (m_param
->bEmitInfoSEI
)
1214 char *opts
= x265_param2string(m_param
);
1217 char *buffer
= X265_MALLOC(char, strlen(opts
) + strlen(x265_version_str
) +
1218 strlen(x265_build_info_str
) + 200);
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
);
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
);
1241 if (m_param
->bEmitHRDSEI
|| !!m_param
->interlaceMode
)
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;
1249 sei
.write(bs
, m_sps
);
1250 bs
.writeByteAlignment();
1251 list
.serialize(NAL_UNIT_PREFIX_SEI
, bs
);
1255 void Encoder::initSPS(SPS
*sps
)
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;
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
;
1272 sps
->log2MinCodingBlockSize
= g_maxLog2CUSize
- g_maxCUDepth
;
1273 sps
->log2DiffMaxMinCodingBlockSize
= g_maxCUDepth
;
1275 sps
->quadtreeTULog2MaxSize
= X265_MIN(g_maxLog2CUSize
, 5);
1276 sps
->quadtreeTULog2MinSize
= 2;
1277 sps
->quadtreeTUMaxDepthInter
= m_param
->tuQTMaxInterDepth
;
1278 sps
->quadtreeTUMaxDepthIntra
= m_param
->tuQTMaxIntraDepth
;
1280 sps
->bUseSAO
= m_param
->bEnableSAO
;
1282 sps
->bUseAMP
= m_param
->bEnableAMP
;
1283 sps
->maxAMPDepth
= m_param
->bEnableAMP
? g_maxCUDepth
: 0;
1285 sps
->maxDecPicBuffering
= m_vps
.maxDecPicBuffering
;
1286 sps
->numReorderPics
= m_vps
.numReorderPics
;
1288 sps
->bUseStrongIntraSmoothing
= m_param
->bEnableStrongIntraSmoothing
;
1289 sps
->bTemporalMVPEnabled
= m_param
->bEnableTemporalMvp
;
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
;
1297 vui
.overscanInfoPresentFlag
= m_param
->vui
.bEnableOverscanInfoPresentFlag
;
1298 vui
.overscanAppropriateFlag
= m_param
->vui
.bEnableOverscanAppropriateFlag
;
1300 vui
.videoSignalTypePresentFlag
= m_param
->vui
.bEnableVideoSignalTypePresentFlag
;
1301 vui
.videoFormat
= m_param
->vui
.videoFormat
;
1302 vui
.videoFullRangeFlag
= m_param
->vui
.bEnableVideoFullRangeFlag
;
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
;
1309 vui
.chromaLocInfoPresentFlag
= m_param
->vui
.bEnableChromaLocInfoPresentFlag
;
1310 vui
.chromaSampleLocTypeTopField
= m_param
->vui
.chromaSampleLocTypeTopField
;
1311 vui
.chromaSampleLocTypeBottomField
= m_param
->vui
.chromaSampleLocTypeBottomField
;
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
;
1319 vui
.frameFieldInfoPresentFlag
= !!m_param
->interlaceMode
;
1320 vui
.fieldSeqFlag
= !!m_param
->interlaceMode
;
1322 vui
.hrdParametersPresentFlag
= m_param
->bEmitHRDSEI
;
1324 vui
.timingInfo
.numUnitsInTick
= m_param
->fpsDenom
;
1325 vui
.timingInfo
.timeScale
= m_param
->fpsNum
;
1328 void Encoder::initPPS(PPS
*pps
)
1330 bool bIsVbv
= m_param
->rc
.vbvBufferSize
> 0 && m_param
->rc
.vbvMaxBitrate
> 0;
1332 if (!m_param
->bLossless
&& (m_param
->rc
.aqMode
|| bIsVbv
))
1334 pps
->bUseDQP
= true;
1335 pps
->maxCuDQPDepth
= 0; /* TODO: make configurable? */
1339 pps
->bUseDQP
= false;
1340 pps
->maxCuDQPDepth
= 0;
1343 pps
->chromaQpOffset
[0] = m_param
->cbQpOffset
;
1344 pps
->chromaQpOffset
[1] = m_param
->crQpOffset
;
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
;
1353 pps
->bDeblockingFilterControlPresent
= !m_param
->bEnableLoopFilter
|| m_param
->deblockingFilterBetaOffset
|| m_param
->deblockingFilterTCOffset
;
1354 pps
->bPicDisableDeblockingFilter
= !m_param
->bEnableLoopFilter
;
1355 pps
->deblockingFilterBetaOffsetDiv2
= m_param
->deblockingFilterBetaOffset
;
1356 pps
->deblockingFilterTcOffsetDiv2
= m_param
->deblockingFilterTCOffset
;
1358 pps
->bEntropyCodingSyncEnabled
= m_param
->bEnableWavefront
;
1361 void Encoder::configure(x265_param
*p
)
1365 if (p
->keyframeMax
< 0)
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;
1373 else if (p
->keyframeMax
<= 1)
1375 // disable lookahead for all-intra encodes
1376 p
->bFrameAdaptive
= 0;
1379 if (!p
->keyframeMin
)
1381 double fps
= (double)p
->fpsNum
/ p
->fpsDenom
;
1382 p
->keyframeMin
= X265_MIN((int)fps
, p
->keyframeMax
/ 10);
1384 p
->keyframeMin
= X265_MAX(1, X265_MIN(p
->keyframeMin
, p
->keyframeMax
/ 2 + 1));
1386 if (p
->bBPyramid
&& !p
->bframes
)
1389 /* Disable features which are not supported by the current RD level */
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;
1398 if (p
->psyRdoq
> 0) /* impossible */
1399 x265_log(p
, X265_LOG_WARNING
, "--psy-rdoq disabled, requires --rdlevel 4 or higher\n");
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;
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;
1416 if (p
->psyRd
> 0) /* impossible */
1417 x265_log(p
, X265_LOG_WARNING
, "--psy-rd disabled, requires --rdlevel 2 or higher\n");
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;
1425 if (!p
->bEnableRectInter
) /* not useful */
1426 p
->bEnableAMP
= false;
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
)
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
1443 if (p
->rc
.rateControlMode
== X265_RC_CQP
)
1445 p
->rc
.aqMode
= X265_AQ_NONE
;
1448 p
->rc
.aqStrength
= 0;
1451 if (p
->rc
.aqMode
== 0 && p
->rc
.cuTree
)
1453 p
->rc
.aqMode
= X265_AQ_VARIANCE
;
1454 p
->rc
.aqStrength
= 0.0;
1457 if (p
->lookaheadDepth
== 0 && p
->rc
.cuTree
&& !p
->rc
.bStatRead
)
1459 x265_log(p
, X265_LOG_WARNING
, "cuTree disabled, requires lookahead to be enabled\n");
1463 if (p
->rc
.aqStrength
== 0 && p
->rc
.cuTree
== 0)
1464 p
->rc
.aqMode
= X265_AQ_NONE
;
1466 if (p
->rc
.aqMode
== X265_AQ_NONE
&& p
->rc
.cuTree
== 0)
1467 p
->rc
.aqStrength
= 0;
1469 if (p
->internalCsp
!= X265_CSP_I420
)
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");
1475 if (p
->scalingLists
&& p
->internalCsp
== X265_CSP_I444
)
1477 x265_log(p
, X265_LOG_WARNING
, "Scaling lists are not yet supported for 4:4:4 color space\n");
1478 p
->scalingLists
= 0;
1481 if (p
->interlaceMode
)
1482 x265_log(p
, X265_LOG_WARNING
, "Support for interlaced video is experimental\n");
1484 if (p
->rc
.rfConstantMin
> p
->rc
.rfConstant
)
1486 x265_log(m_param
, X265_LOG_WARNING
, "CRF min must be less than CRF\n");
1487 p
->rc
.rfConstantMin
= 0;
1490 m_bframeDelay
= p
->bframes
? (p
->bBPyramid
? 2 : 1) : 0;
1492 p
->bFrameBias
= X265_MIN(X265_MAX(-90, p
->bFrameBias
), 100);
1494 if (p
->logLevel
< X265_LOG_INFO
)
1496 /* don't measure these metrics if they will not be reported */
1500 /* Warn users trying to measure PSNR/SSIM with psy opts on. */
1501 if (p
->bEnablePsnr
|| p
->bEnableSsim
)
1503 const char *s
= NULL
;
1505 if (p
->psyRd
|| p
->psyRdoq
)
1507 s
= p
->bEnablePsnr
? "psnr" : "ssim";
1508 x265_log(p
, X265_LOG_WARNING
, "--%s used with psy on: results will be invalid!\n", s
);
1510 else if (!p
->rc
.aqMode
&& p
->bEnableSsim
)
1512 x265_log(p
, X265_LOG_WARNING
, "--ssim used with AQ off: results will be invalid!\n");
1515 else if (p
->rc
.aqStrength
> 0 && p
->bEnablePsnr
)
1517 x265_log(p
, X265_LOG_WARNING
, "--psnr used with AQ on: results will be invalid!\n");
1521 x265_log(p
, X265_LOG_WARNING
, "--tune %s should be used if attempting to benchmark %s!\n", s
, s
);
1524 /* initialize the conformance window */
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;
1531 /* set pad size if width is not multiple of the minimum CU size */
1532 if (p
->sourceWidth
& (MIN_CU_SIZE
- 1))
1534 uint32_t rem
= p
->sourceWidth
& (MIN_CU_SIZE
- 1);
1535 uint32_t padsize
= MIN_CU_SIZE
- rem
;
1536 p
->sourceWidth
+= padsize
;
1538 m_conformanceWindow
.bEnabled
= true;
1539 m_conformanceWindow
.rightOffset
= padsize
;
1542 /* set pad size if height is not multiple of the minimum CU size */
1543 if (p
->sourceHeight
& (MIN_CU_SIZE
- 1))
1545 uint32_t rem
= p
->sourceHeight
& (MIN_CU_SIZE
- 1);
1546 uint32_t padsize
= MIN_CU_SIZE
- rem
;
1547 p
->sourceHeight
+= padsize
;
1549 m_conformanceWindow
.bEnabled
= true;
1550 m_conformanceWindow
.bottomOffset
= padsize
;
1552 if (p
->bDistributeModeAnalysis
&& p
->analysisMode
)
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");
1559 void Encoder::allocAnalysis(x265_analysis_data
* analysis
)
1561 if (analysis
->sliceType
== X265_TYPE_IDR
|| analysis
->sliceType
== X265_TYPE_I
)
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
;
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
;
1579 freeAnalysis(analysis
);
1583 void Encoder::freeAnalysis(x265_analysis_data
* analysis
)
1585 if (analysis
->sliceType
== X265_TYPE_IDR
|| analysis
->sliceType
== X265_TYPE_I
)
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
);
1593 X265_FREE(analysis
->interData
);
1596 void Encoder::readAnalysisFile(x265_analysis_data
* analysis
, int curPoc
)
1599 #define X265_FREAD(val, size, readSize, fileOffset)\
1600 if (fread(val, size, readSize, fileOffset) != readSize)\
1602 x265_log(NULL, X265_LOG_ERROR, "Error reading analysis data\n");\
1603 freeAnalysis(analysis);\
1608 static uint64_t consumedBytes = 0;
1609 static uint64_t totalConsumedBytes
= 0;
1610 fseeko(m_analysisFile
, totalConsumedBytes
, SEEK_SET
);
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
);
1616 uint64_t currentOffset
= totalConsumedBytes
;
1618 /* Seeking to the right frame Record */
1619 while (poc
!= curPoc
&& !feof(m_analysisFile
))
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
);
1627 if (poc
!= curPoc
|| feof(m_analysisFile
))
1629 x265_log(NULL
, X265_LOG_WARNING
, "Error reading analysis data: Cannot find POC %d\n", curPoc
);
1630 freeAnalysis(analysis
);
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
);
1641 /* Memory is allocated for inter and intra analysis data based on the slicetype */
1642 allocAnalysis(analysis
);
1644 if (analysis
->sliceType
== X265_TYPE_IDR
|| analysis
->sliceType
== X265_TYPE_I
)
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
;
1652 else if (analysis
->sliceType
== X265_TYPE_P
)
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
;
1660 X265_FREAD(analysis
->interData
, sizeof(analysis_inter_data
), analysis
->numCUsInFrame
* X265_MAX_PRED_MODE_PER_CTU
* 2, m_analysisFile
);
1661 consumedBytes
+= frameRecordSize
;
1666 void Encoder::writeAnalysisFile(x265_analysis_data
* analysis
)
1669 #define X265_FWRITE(val, size, writeSize, fileOffset)\
1670 if (fwrite(val, size, writeSize, fileOffset) < writeSize)\
1672 x265_log(NULL, X265_LOG_ERROR, "Error writing analysis data\n");\
1673 freeAnalysis(analysis);\
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
;
1686 analysis
->frameRecordSize
+= sizeof(analysis_inter_data
) * analysis
->numCUsInFrame
* X265_MAX_PRED_MODE_PER_CTU
* 2;
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
);
1694 if (analysis
->sliceType
== X265_TYPE_IDR
|| analysis
->sliceType
== X265_TYPE_I
)
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
);
1700 else if (analysis
->sliceType
== X265_TYPE_P
)
1702 X265_FWRITE(analysis
->interData
, sizeof(analysis_inter_data
), analysis
->numCUsInFrame
* X265_MAX_PRED_MODE_PER_CTU
, m_analysisFile
);
1706 X265_FWRITE(analysis
->interData
, sizeof(analysis_inter_data
), analysis
->numCUsInFrame
* X265_MAX_PRED_MODE_PER_CTU
* 2, m_analysisFile
);