/src/x265/source/encoder/api.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * Copyright (C) 2013-2020 MulticoreWare, Inc |
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 "bitstream.h" |
26 | | #include "param.h" |
27 | | |
28 | | #include "encoder.h" |
29 | | #include "entropy.h" |
30 | | #include "level.h" |
31 | | #include "nal.h" |
32 | | #include "bitcost.h" |
33 | | #include "svt.h" |
34 | | |
35 | | #if ENABLE_LIBVMAF |
36 | | #include "libvmaf.h" |
37 | | #endif |
38 | | |
39 | | /* multilib namespace reflectors */ |
40 | | #if LINKED_8BIT |
41 | | namespace x265_8bit { |
42 | | const x265_api* x265_api_get(int bitDepth); |
43 | | const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err); |
44 | | } |
45 | | #endif |
46 | | |
47 | | #if LINKED_10BIT |
48 | | namespace x265_10bit { |
49 | | const x265_api* x265_api_get(int bitDepth); |
50 | | const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err); |
51 | | } |
52 | | #endif |
53 | | |
54 | | #if LINKED_12BIT |
55 | | namespace x265_12bit { |
56 | | const x265_api* x265_api_get(int bitDepth); |
57 | | const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err); |
58 | | } |
59 | | #endif |
60 | | |
61 | | #if EXPORT_C_API |
62 | | /* these functions are exported as C functions (default) */ |
63 | | using namespace X265_NS; |
64 | | extern "C" { |
65 | | #else |
66 | | /* these functions exist within private namespace (multilib) */ |
67 | | namespace X265_NS { |
68 | | #endif |
69 | | |
70 | | static const char* summaryCSVHeader = |
71 | | "Command, Date/Time, Elapsed Time, FPS, Bitrate, " |
72 | | "Y PSNR, U PSNR, V PSNR, Global PSNR, SSIM, SSIM (dB), " |
73 | | "I count, I ave-QP, I kbps, I-PSNR Y, I-PSNR U, I-PSNR V, I-SSIM (dB), " |
74 | | "P count, P ave-QP, P kbps, P-PSNR Y, P-PSNR U, P-PSNR V, P-SSIM (dB), " |
75 | | "B count, B ave-QP, B kbps, B-PSNR Y, B-PSNR U, B-PSNR V, B-SSIM (dB), "; |
76 | | x265_encoder *x265_encoder_open(x265_param *p) |
77 | 698 | { |
78 | 698 | if (!p) |
79 | 0 | return NULL; |
80 | | |
81 | | #if _MSC_VER |
82 | | #pragma warning(disable: 4127) // conditional expression is constant, yes I know |
83 | | #endif |
84 | | |
85 | | #if HIGH_BIT_DEPTH |
86 | | if (X265_DEPTH != 10 && X265_DEPTH != 12) |
87 | | #else |
88 | 698 | if (X265_DEPTH != 8) |
89 | 0 | #endif |
90 | 0 | { |
91 | 0 | x265_log(p, X265_LOG_ERROR, "Build error, internal bit depth mismatch\n"); |
92 | 0 | return NULL; |
93 | 0 | } |
94 | | |
95 | 698 | Encoder* encoder = NULL; |
96 | 698 | x265_param* param = PARAM_NS::x265_param_alloc(); |
97 | 698 | x265_param* latestParam = PARAM_NS::x265_param_alloc(); |
98 | 698 | x265_param* zoneParam = PARAM_NS::x265_param_alloc(); |
99 | | |
100 | 698 | if(param) PARAM_NS::x265_param_default(param); |
101 | 698 | if(latestParam) PARAM_NS::x265_param_default(latestParam); |
102 | 698 | if(zoneParam) PARAM_NS::x265_param_default(zoneParam); |
103 | | |
104 | 698 | if (!param || !latestParam || !zoneParam) |
105 | 0 | goto fail; |
106 | 698 | if (p->rc.zoneCount || p->rc.zonefileCount) |
107 | 0 | { |
108 | 0 | int zoneCount = p->rc.zonefileCount ? p->rc.zonefileCount : p->rc.zoneCount; |
109 | 0 | param->rc.zones = x265_zone_alloc(zoneCount, !!p->rc.zonefileCount); |
110 | 0 | latestParam->rc.zones = x265_zone_alloc(zoneCount, !!p->rc.zonefileCount); |
111 | 0 | zoneParam->rc.zones = x265_zone_alloc(zoneCount, !!p->rc.zonefileCount); |
112 | 0 | } |
113 | | |
114 | 698 | x265_copy_params(param, p); |
115 | 698 | x265_copy_params(latestParam, p); |
116 | 698 | x265_copy_params(zoneParam, p); |
117 | 698 | x265_log(param, X265_LOG_INFO, "HEVC encoder version %s\n", PFX(version_str)); |
118 | 698 | x265_log(param, X265_LOG_INFO, "build info %s\n", PFX(build_info_str)); |
119 | | |
120 | 698 | encoder = new Encoder; |
121 | | |
122 | | #ifdef SVT_HEVC |
123 | | |
124 | | if (param->bEnableSvtHevc) |
125 | | { |
126 | | EB_ERRORTYPE return_error = EB_ErrorNone; |
127 | | int ret = 0; |
128 | | |
129 | | svt_initialise_app_context(encoder); |
130 | | ret = svt_initialise_input_buffer(encoder); |
131 | | if (!ret) |
132 | | { |
133 | | x265_log(param, X265_LOG_ERROR, "SVT-HEVC Encoder: Unable to allocate input buffer \n"); |
134 | | goto fail; |
135 | | } |
136 | | |
137 | | // Create Encoder Handle |
138 | | return_error = EbInitHandle(&encoder->m_svtAppData->svtEncoderHandle, encoder->m_svtAppData, encoder->m_svtAppData->svtHevcParams); |
139 | | if (return_error != EB_ErrorNone) |
140 | | { |
141 | | x265_log(param, X265_LOG_ERROR, "SVT-HEVC Encoder: Unable to initialise encoder handle \n"); |
142 | | goto fail; |
143 | | } |
144 | | |
145 | | memcpy(encoder->m_svtAppData->svtHevcParams, param->svtHevcParam, sizeof(EB_H265_ENC_CONFIGURATION)); |
146 | | |
147 | | // Send over all configuration parameters |
148 | | return_error = EbH265EncSetParameter(encoder->m_svtAppData->svtEncoderHandle, encoder->m_svtAppData->svtHevcParams); |
149 | | if (return_error != EB_ErrorNone) |
150 | | { |
151 | | x265_log(param, X265_LOG_ERROR, "SVT-HEVC Encoder: Error while configuring encoder parameters \n"); |
152 | | goto fail; |
153 | | } |
154 | | |
155 | | // Init Encoder |
156 | | return_error = EbInitEncoder(encoder->m_svtAppData->svtEncoderHandle); |
157 | | if (return_error != EB_ErrorNone) |
158 | | { |
159 | | x265_log(param, X265_LOG_ERROR, "SVT-HEVC Encoder: Encoder init failed \n"); |
160 | | goto fail; |
161 | | } |
162 | | |
163 | | memcpy(param->svtHevcParam, encoder->m_svtAppData->svtHevcParams, sizeof(EB_H265_ENC_CONFIGURATION)); |
164 | | encoder->m_param = param; |
165 | | return encoder; |
166 | | } |
167 | | #endif |
168 | | |
169 | 698 | x265_setup_primitives(param); |
170 | | |
171 | 698 | if (x265_check_params(param)) |
172 | 0 | goto fail; |
173 | | |
174 | 698 | if (!param->rc.bEnableSlowFirstPass) |
175 | 0 | PARAM_NS::x265_param_apply_fastfirstpass(param); |
176 | | |
177 | | // may change params for auto-detect, etc |
178 | 698 | encoder->configure(param); |
179 | 698 | if (encoder->m_aborted) |
180 | 0 | goto fail; |
181 | | // may change rate control and CPB params |
182 | 698 | if (!enforceLevel(*param, encoder->m_vps)) |
183 | 0 | goto fail; |
184 | | |
185 | | // will detect and set profile/tier/level in VPS |
186 | 698 | determineLevel(*param, encoder->m_vps); |
187 | | |
188 | 698 | if (!param->bAllowNonConformance && encoder->m_vps.ptl.profileIdc == Profile::NONE) |
189 | 0 | { |
190 | 0 | x265_log(param, X265_LOG_INFO, "non-conformant bitstreams not allowed (--allow-non-conformance)\n"); |
191 | 0 | goto fail; |
192 | 0 | } |
193 | | |
194 | 698 | encoder->create(); |
195 | 698 | p->frameNumThreads = encoder->m_param->frameNumThreads; |
196 | | |
197 | 698 | if (!param->bResetZoneConfig) |
198 | 0 | { |
199 | 0 | param->rc.zones = X265_MALLOC(x265_zone, param->rc.zonefileCount); |
200 | 0 | for (int i = 0; i < param->rc.zonefileCount; i++) |
201 | 0 | { |
202 | 0 | param->rc.zones[i].zoneParam = X265_MALLOC(x265_param, 1); |
203 | 0 | memcpy(param->rc.zones[i].zoneParam, param, sizeof(x265_param)); |
204 | 0 | param->rc.zones[i].relativeComplexity = X265_MALLOC(double, param->reconfigWindowSize); |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | 698 | memcpy(zoneParam, param, sizeof(x265_param)); |
209 | 698 | for (int i = 0; i < param->rc.zonefileCount; i++) |
210 | 0 | { |
211 | 0 | param->rc.zones[i].startFrame = -1; |
212 | 0 | encoder->configureZone(zoneParam, param->rc.zones[i].zoneParam); |
213 | 0 | } |
214 | | |
215 | | /* Try to open CSV file handle */ |
216 | 698 | if (encoder->m_param->csvfn) |
217 | 0 | { |
218 | 0 | encoder->m_param->csvfpt = x265_csvlog_open(encoder->m_param); |
219 | 0 | if (!encoder->m_param->csvfpt) |
220 | 0 | { |
221 | 0 | x265_log(encoder->m_param, X265_LOG_ERROR, "Unable to open CSV log file <%s>, aborting\n", encoder->m_param->csvfn); |
222 | 0 | encoder->m_aborted = true; |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | 698 | encoder->m_latestParam = latestParam; |
227 | 698 | x265_copy_params(latestParam, param); |
228 | 698 | if (encoder->m_aborted) |
229 | 0 | goto fail; |
230 | | |
231 | 698 | x265_print_params(param); |
232 | 698 | return encoder; |
233 | | |
234 | 0 | fail: |
235 | 0 | delete encoder; |
236 | 0 | PARAM_NS::x265_param_free(param); |
237 | 0 | PARAM_NS::x265_param_free(latestParam); |
238 | 0 | PARAM_NS::x265_param_free(zoneParam); |
239 | 0 | return NULL; |
240 | 698 | } |
241 | | |
242 | | int x265_encoder_headers(x265_encoder *enc, x265_nal **pp_nal, uint32_t *pi_nal) |
243 | 0 | { |
244 | 0 | if (pp_nal && enc) |
245 | 0 | { |
246 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
247 | | #ifdef SVT_HEVC |
248 | | if (encoder->m_param->bEnableSvtHevc) |
249 | | { |
250 | | EB_ERRORTYPE return_error; |
251 | | EB_BUFFERHEADERTYPE* outputPtr; |
252 | | return_error = EbH265EncStreamHeader(encoder->m_svtAppData->svtEncoderHandle, &outputPtr); |
253 | | if (return_error != EB_ErrorNone) |
254 | | { |
255 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while generating stream headers \n"); |
256 | | encoder->m_aborted = true; |
257 | | return -1; |
258 | | } |
259 | | |
260 | | //Copy data from output packet to NAL |
261 | | encoder->m_nalList.m_nal[0].payload = outputPtr->pBuffer; |
262 | | encoder->m_nalList.m_nal[0].sizeBytes = outputPtr->nFilledLen; |
263 | | *pp_nal = &encoder->m_nalList.m_nal[0]; |
264 | | *pi_nal = 1; |
265 | | encoder->m_svtAppData->byteCount += outputPtr->nFilledLen; |
266 | | |
267 | | // Release the output buffer |
268 | | EbH265ReleaseOutBuffer(&outputPtr); |
269 | | |
270 | | return pp_nal[0]->sizeBytes; |
271 | | } |
272 | | #endif |
273 | |
|
274 | 0 | Entropy sbacCoder; |
275 | 0 | Bitstream bs; |
276 | 0 | if (encoder->m_param->rc.bStatRead && encoder->m_param->bMultiPassOptRPS) |
277 | 0 | { |
278 | 0 | if (!encoder->computeSPSRPSIndex()) |
279 | 0 | { |
280 | 0 | encoder->m_aborted = true; |
281 | 0 | return -1; |
282 | 0 | } |
283 | 0 | } |
284 | 0 | encoder->getStreamHeaders(encoder->m_nalList, sbacCoder, bs); |
285 | 0 | *pp_nal = &encoder->m_nalList.m_nal[0]; |
286 | 0 | if (pi_nal) *pi_nal = encoder->m_nalList.m_numNal; |
287 | 0 | return encoder->m_nalList.m_occupancy; |
288 | 0 | } |
289 | | |
290 | 0 | if (enc) |
291 | 0 | { |
292 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
293 | 0 | encoder->m_aborted = true; |
294 | 0 | } |
295 | 0 | return -1; |
296 | 0 | } |
297 | | |
298 | | void x265_encoder_parameters(x265_encoder *enc, x265_param *out) |
299 | 0 | { |
300 | 0 | if (enc && out) |
301 | 0 | { |
302 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
303 | 0 | x265_copy_params(out, encoder->m_param); |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | | int x265_encoder_reconfig(x265_encoder* enc, x265_param* param_in) |
308 | 0 | { |
309 | 0 | if (!enc || !param_in) |
310 | 0 | return -1; |
311 | 0 | x265_param save; |
312 | 0 | Encoder* encoder = static_cast<Encoder*>(enc); |
313 | 0 | if (encoder->m_param->csvfn == NULL && param_in->csvfpt != NULL) |
314 | 0 | encoder->m_param->csvfpt = param_in->csvfpt; |
315 | 0 | if (encoder->m_latestParam->forceFlush != param_in->forceFlush) |
316 | 0 | return encoder->reconfigureParam(encoder->m_latestParam, param_in); |
317 | 0 | bool isReconfigureRc = encoder->isReconfigureRc(encoder->m_latestParam, param_in); |
318 | 0 | if ((encoder->m_reconfigure && !isReconfigureRc) || (encoder->m_reconfigureRc && isReconfigureRc)) /* Reconfigure in progress */ |
319 | 0 | return 1; |
320 | 0 | if (encoder->m_latestParam->rc.zoneCount || encoder->m_latestParam->rc.zonefileCount) |
321 | 0 | { |
322 | 0 | int zoneCount = encoder->m_latestParam->rc.zonefileCount ? encoder->m_latestParam->rc.zonefileCount : encoder->m_latestParam->rc.zoneCount; |
323 | 0 | save.rc.zones = x265_zone_alloc(zoneCount, !!encoder->m_latestParam->rc.zonefileCount); |
324 | 0 | } |
325 | 0 | x265_copy_params(&save, encoder->m_latestParam); |
326 | 0 | int ret = encoder->reconfigureParam(encoder->m_latestParam, param_in); |
327 | 0 | if (ret) |
328 | 0 | { |
329 | | /* reconfigure failed, recover saved param set */ |
330 | 0 | x265_copy_params(encoder->m_latestParam, &save); |
331 | 0 | ret = -1; |
332 | 0 | } |
333 | 0 | else |
334 | 0 | { |
335 | 0 | encoder->configure(encoder->m_latestParam); |
336 | 0 | if (encoder->m_latestParam->scalingLists && encoder->m_latestParam->scalingLists != encoder->m_param->scalingLists) |
337 | 0 | { |
338 | 0 | if (encoder->m_param->bRepeatHeaders) |
339 | 0 | { |
340 | 0 | if (encoder->m_scalingList.parseScalingList(encoder->m_latestParam->scalingLists)) |
341 | 0 | { |
342 | 0 | x265_copy_params(encoder->m_latestParam, &save); |
343 | 0 | return -1; |
344 | 0 | } |
345 | 0 | encoder->m_scalingList.setupQuantMatrices(encoder->m_param->internalCsp); |
346 | 0 | } |
347 | 0 | else |
348 | 0 | { |
349 | 0 | x265_log(encoder->m_param, X265_LOG_ERROR, "Repeat headers is turned OFF, cannot reconfigure scalinglists\n"); |
350 | 0 | x265_copy_params(encoder->m_latestParam, &save); |
351 | 0 | return -1; |
352 | 0 | } |
353 | 0 | } |
354 | 0 | if (!isReconfigureRc) |
355 | 0 | encoder->m_reconfigure = true; |
356 | 0 | else if (encoder->m_reconfigureRc) |
357 | 0 | { |
358 | 0 | VPS saveVPS; |
359 | 0 | memcpy(&saveVPS.ptl, &encoder->m_vps.ptl, sizeof(saveVPS.ptl)); |
360 | 0 | determineLevel(*encoder->m_latestParam, encoder->m_vps); |
361 | 0 | if (saveVPS.ptl.profileIdc != encoder->m_vps.ptl.profileIdc || saveVPS.ptl.levelIdc != encoder->m_vps.ptl.levelIdc |
362 | 0 | || saveVPS.ptl.tierFlag != encoder->m_vps.ptl.tierFlag) |
363 | 0 | { |
364 | 0 | x265_log(encoder->m_param, X265_LOG_WARNING, "Profile/Level/Tier has changed from %d/%d/%s to %d/%d/%s.Cannot reconfigure rate-control.\n", |
365 | 0 | saveVPS.ptl.profileIdc, saveVPS.ptl.levelIdc, saveVPS.ptl.tierFlag ? "High" : "Main", encoder->m_vps.ptl.profileIdc, |
366 | 0 | encoder->m_vps.ptl.levelIdc, encoder->m_vps.ptl.tierFlag ? "High" : "Main"); |
367 | 0 | x265_copy_params(encoder->m_latestParam, &save); |
368 | 0 | memcpy(&encoder->m_vps.ptl, &saveVPS.ptl, sizeof(saveVPS.ptl)); |
369 | 0 | encoder->m_reconfigureRc = false; |
370 | 0 | } |
371 | 0 | } |
372 | 0 | encoder->printReconfigureParams(); |
373 | 0 | } |
374 | | /* Zones support modifying num of Refs. Requires determining level at each zone start*/ |
375 | 0 | if (encoder->m_param->rc.zonefileCount) |
376 | 0 | determineLevel(*encoder->m_latestParam, encoder->m_vps); |
377 | 0 | return ret; |
378 | 0 | } |
379 | | |
380 | | |
381 | | int x265_encoder_reconfig_zone(x265_encoder* enc, x265_zone* zone_in) |
382 | 0 | { |
383 | 0 | if (!enc || !zone_in) |
384 | 0 | return -1; |
385 | | |
386 | 0 | Encoder* encoder = static_cast<Encoder*>(enc); |
387 | 0 | int read = encoder->zoneReadCount[encoder->m_zoneIndex].get(); |
388 | 0 | int write = encoder->zoneWriteCount[encoder->m_zoneIndex].get(); |
389 | |
|
390 | 0 | x265_zone* zone = &(encoder->m_param->rc).zones[encoder->m_zoneIndex]; |
391 | 0 | x265_param* zoneParam = zone->zoneParam; |
392 | |
|
393 | 0 | if (write && (read < write)) |
394 | 0 | { |
395 | 0 | read = encoder->zoneReadCount[encoder->m_zoneIndex].waitForChange(read); |
396 | 0 | } |
397 | |
|
398 | 0 | zone->startFrame = zone_in->startFrame; |
399 | 0 | zoneParam->rc.bitrate = zone_in->zoneParam->rc.bitrate; |
400 | 0 | zoneParam->rc.vbvMaxBitrate = zone_in->zoneParam->rc.vbvMaxBitrate; |
401 | 0 | memcpy(zone->relativeComplexity, zone_in->relativeComplexity, sizeof(double) * encoder->m_param->reconfigWindowSize); |
402 | | |
403 | 0 | encoder->zoneWriteCount[encoder->m_zoneIndex].incr(); |
404 | 0 | encoder->m_zoneIndex++; |
405 | 0 | encoder->m_zoneIndex %= encoder->m_param->rc.zonefileCount; |
406 | |
|
407 | 0 | return 0; |
408 | 0 | } |
409 | | |
410 | | int x265_encoder_encode(x265_encoder *enc, x265_nal **pp_nal, uint32_t *pi_nal, x265_picture *pic_in, x265_picture *pic_out) |
411 | 2.04k | { |
412 | 2.04k | if (!enc) |
413 | 0 | return -1; |
414 | | |
415 | 2.04k | Encoder *encoder = static_cast<Encoder*>(enc); |
416 | 2.04k | int numEncoded; |
417 | | |
418 | | #ifdef SVT_HEVC |
419 | | EB_ERRORTYPE return_error; |
420 | | if (encoder->m_param->bEnableSvtHevc) |
421 | | { |
422 | | static unsigned char picSendDone = 0; |
423 | | numEncoded = 0; |
424 | | static int codedNal = 0, eofReached = 0; |
425 | | EB_H265_ENC_CONFIGURATION* svtParam = (EB_H265_ENC_CONFIGURATION*)encoder->m_svtAppData->svtHevcParams; |
426 | | if (pic_in) |
427 | | { |
428 | | if (pic_in->colorSpace == X265_CSP_I420) // SVT-HEVC supports only yuv420p color space |
429 | | { |
430 | | EB_BUFFERHEADERTYPE *inputPtr = encoder->m_svtAppData->inputPictureBuffer; |
431 | | |
432 | | if (pic_in->framesize) inputPtr->nFilledLen = (uint32_t)pic_in->framesize; |
433 | | inputPtr->nFlags = 0; |
434 | | inputPtr->pts = pic_in->pts; |
435 | | inputPtr->dts = pic_in->dts; |
436 | | inputPtr->sliceType = EB_INVALID_PICTURE; |
437 | | |
438 | | EB_H265_ENC_INPUT *inputData = (EB_H265_ENC_INPUT*) inputPtr->pBuffer; |
439 | | inputData->luma = (unsigned char*) pic_in->planes[0]; |
440 | | inputData->cb = (unsigned char*) pic_in->planes[1]; |
441 | | inputData->cr = (unsigned char*) pic_in->planes[2]; |
442 | | |
443 | | inputData->yStride = encoder->m_param->sourceWidth; |
444 | | inputData->cbStride = encoder->m_param->sourceWidth >> 1; |
445 | | inputData->crStride = encoder->m_param->sourceWidth >> 1; |
446 | | |
447 | | inputData->lumaExt = NULL; |
448 | | inputData->cbExt = NULL; |
449 | | inputData->crExt = NULL; |
450 | | |
451 | | if (pic_in->rpu.payloadSize) |
452 | | { |
453 | | inputData->dolbyVisionRpu.payload = X265_MALLOC(uint8_t, 1024); |
454 | | memcpy(inputData->dolbyVisionRpu.payload, pic_in->rpu.payload, pic_in->rpu.payloadSize); |
455 | | inputData->dolbyVisionRpu.payloadSize = pic_in->rpu.payloadSize; |
456 | | inputData->dolbyVisionRpu.payloadType = NAL_UNIT_UNSPECIFIED; |
457 | | } |
458 | | else |
459 | | { |
460 | | inputData->dolbyVisionRpu.payload = NULL; |
461 | | inputData->dolbyVisionRpu.payloadSize = 0; |
462 | | } |
463 | | |
464 | | // Send the picture to the encoder |
465 | | return_error = EbH265EncSendPicture(encoder->m_svtAppData->svtEncoderHandle, inputPtr); |
466 | | |
467 | | if (return_error != EB_ErrorNone) |
468 | | { |
469 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while encoding \n"); |
470 | | numEncoded = -1; |
471 | | goto fail; |
472 | | } |
473 | | } |
474 | | else |
475 | | { |
476 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC Encoder accepts only yuv420p input \n"); |
477 | | numEncoded = -1; |
478 | | goto fail; |
479 | | } |
480 | | } |
481 | | else if (!picSendDone) //Encoder flush |
482 | | { |
483 | | picSendDone = 1; |
484 | | EB_BUFFERHEADERTYPE inputPtrLast; |
485 | | inputPtrLast.nAllocLen = 0; |
486 | | inputPtrLast.nFilledLen = 0; |
487 | | inputPtrLast.nTickCount = 0; |
488 | | inputPtrLast.pAppPrivate = NULL; |
489 | | inputPtrLast.nFlags = EB_BUFFERFLAG_EOS; |
490 | | inputPtrLast.pBuffer = NULL; |
491 | | |
492 | | return_error = EbH265EncSendPicture(encoder->m_svtAppData->svtEncoderHandle, &inputPtrLast); |
493 | | if (return_error != EB_ErrorNone) |
494 | | { |
495 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while encoding \n"); |
496 | | numEncoded = -1; |
497 | | goto fail; |
498 | | } |
499 | | } |
500 | | |
501 | | if (eofReached && svtParam->codeEosNal == 0 && !codedNal) |
502 | | { |
503 | | EB_BUFFERHEADERTYPE *outputStreamPtr = 0; |
504 | | return_error = EbH265EncEosNal(encoder->m_svtAppData->svtEncoderHandle, &outputStreamPtr); |
505 | | if (return_error == EB_ErrorMax) |
506 | | { |
507 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while encoding \n"); |
508 | | numEncoded = -1; |
509 | | goto fail; |
510 | | } |
511 | | if (return_error != EB_NoErrorEmptyQueue) |
512 | | { |
513 | | if (outputStreamPtr->pBuffer) |
514 | | { |
515 | | //Copy data from output packet to NAL |
516 | | encoder->m_nalList.m_nal[0].payload = outputStreamPtr->pBuffer; |
517 | | encoder->m_nalList.m_nal[0].sizeBytes = outputStreamPtr->nFilledLen; |
518 | | encoder->m_svtAppData->byteCount += outputStreamPtr->nFilledLen; |
519 | | *pp_nal = &encoder->m_nalList.m_nal[0]; |
520 | | *pi_nal = 1; |
521 | | numEncoded = 0; |
522 | | codedNal = 1; |
523 | | return numEncoded; |
524 | | } |
525 | | |
526 | | // Release the output buffer |
527 | | EbH265ReleaseOutBuffer(&outputStreamPtr); |
528 | | } |
529 | | } |
530 | | else if (eofReached) |
531 | | { |
532 | | *pi_nal = 0; |
533 | | return numEncoded; |
534 | | } |
535 | | |
536 | | //Receive Packet |
537 | | EB_BUFFERHEADERTYPE *outputPtr; |
538 | | return_error = EbH265GetPacket(encoder->m_svtAppData->svtEncoderHandle, &outputPtr, picSendDone); |
539 | | if (return_error == EB_ErrorMax) |
540 | | { |
541 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while encoding \n"); |
542 | | numEncoded = -1; |
543 | | goto fail; |
544 | | } |
545 | | |
546 | | if (return_error != EB_NoErrorEmptyQueue) |
547 | | { |
548 | | if (outputPtr->pBuffer) |
549 | | { |
550 | | //Copy data from output packet to NAL |
551 | | encoder->m_nalList.m_nal[0].payload = outputPtr->pBuffer; |
552 | | encoder->m_nalList.m_nal[0].sizeBytes = outputPtr->nFilledLen; |
553 | | encoder->m_svtAppData->byteCount += outputPtr->nFilledLen; |
554 | | encoder->m_svtAppData->outFrameCount++; |
555 | | *pp_nal = &encoder->m_nalList.m_nal[0]; |
556 | | *pi_nal = 1; |
557 | | numEncoded = 1; |
558 | | } |
559 | | |
560 | | eofReached = outputPtr->nFlags & EB_BUFFERFLAG_EOS; |
561 | | |
562 | | // Release the output buffer |
563 | | EbH265ReleaseOutBuffer(&outputPtr); |
564 | | } |
565 | | else if (pi_nal) |
566 | | *pi_nal = 0; |
567 | | |
568 | | pic_out = NULL; |
569 | | |
570 | | fail: |
571 | | if (numEncoded < 0) |
572 | | encoder->m_aborted = true; |
573 | | |
574 | | return numEncoded; |
575 | | } |
576 | | #endif |
577 | | |
578 | | // While flushing, we cannot return 0 until the entire stream is flushed |
579 | 2.04k | do |
580 | 4.46k | { |
581 | 4.46k | numEncoded = encoder->encode(pic_in, pic_out); |
582 | 4.46k | } |
583 | 4.46k | while ((numEncoded == 0 && !pic_in && encoder->m_numDelayedPic && !encoder->m_latestParam->forceFlush) && !encoder->m_externalFlush); |
584 | 2.04k | if (numEncoded) |
585 | 698 | encoder->m_externalFlush = false; |
586 | | |
587 | | // do not allow reuse of these buffers for more than one picture. The |
588 | | // encoder now owns these analysisData buffers. |
589 | 2.04k | if (pic_in) |
590 | 698 | { |
591 | 698 | pic_in->analysisData.wt = NULL; |
592 | 698 | pic_in->analysisData.intraData = NULL; |
593 | 698 | pic_in->analysisData.interData = NULL; |
594 | 698 | pic_in->analysisData.distortionData = NULL; |
595 | 698 | } |
596 | | |
597 | 2.04k | if (pp_nal && numEncoded > 0 && encoder->m_outputCount >= encoder->m_latestParam->chunkStart) |
598 | 698 | { |
599 | 698 | *pp_nal = &encoder->m_nalList.m_nal[0]; |
600 | 698 | if (pi_nal) *pi_nal = encoder->m_nalList.m_numNal; |
601 | 698 | } |
602 | 1.34k | else if (pi_nal) |
603 | 1.34k | *pi_nal = 0; |
604 | | |
605 | 2.04k | if (numEncoded && encoder->m_param->csvLogLevel && encoder->m_outputCount >= encoder->m_latestParam->chunkStart) |
606 | 0 | x265_csvlog_frame(encoder->m_param, pic_out); |
607 | | |
608 | 2.04k | if (numEncoded < 0) |
609 | 0 | encoder->m_aborted = true; |
610 | | |
611 | 2.04k | return numEncoded; |
612 | 2.04k | } |
613 | | |
614 | | void x265_encoder_get_stats(x265_encoder *enc, x265_stats *outputStats, uint32_t statsSizeBytes) |
615 | 0 | { |
616 | 0 | if (enc && outputStats) |
617 | 0 | { |
618 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
619 | 0 | encoder->fetchStats(outputStats, statsSizeBytes); |
620 | 0 | } |
621 | 0 | } |
622 | | #if ENABLE_LIBVMAF |
623 | | void x265_vmaf_encoder_log(x265_encoder* enc, int argc, char **argv, x265_param *param, x265_vmaf_data *vmafdata) |
624 | | { |
625 | | if (enc) |
626 | | { |
627 | | Encoder *encoder = static_cast<Encoder*>(enc); |
628 | | x265_stats stats; |
629 | | stats.aggregateVmafScore = x265_calculate_vmafscore(param, vmafdata); |
630 | | if(vmafdata->reference_file) |
631 | | fclose(vmafdata->reference_file); |
632 | | if(vmafdata->distorted_file) |
633 | | fclose(vmafdata->distorted_file); |
634 | | if(vmafdata) |
635 | | x265_free(vmafdata); |
636 | | encoder->fetchStats(&stats, sizeof(stats)); |
637 | | int padx = encoder->m_sps.conformanceWindow.rightOffset; |
638 | | int pady = encoder->m_sps.conformanceWindow.bottomOffset; |
639 | | x265_csvlog_encode(encoder->m_param, &stats, padx, pady, argc, argv); |
640 | | } |
641 | | } |
642 | | #endif |
643 | | |
644 | | void x265_encoder_log(x265_encoder* enc, int argc, char **argv) |
645 | 0 | { |
646 | 0 | if (enc) |
647 | 0 | { |
648 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
649 | 0 | x265_stats stats; |
650 | 0 | encoder->fetchStats(&stats, sizeof(stats)); |
651 | 0 | int padx = encoder->m_sps.conformanceWindow.rightOffset; |
652 | 0 | int pady = encoder->m_sps.conformanceWindow.bottomOffset; |
653 | 0 | x265_csvlog_encode(encoder->m_param, &stats, padx, pady, argc, argv); |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | | #ifdef SVT_HEVC |
658 | | static void svt_print_summary(x265_encoder *enc) |
659 | | { |
660 | | Encoder *encoder = static_cast<Encoder*>(enc); |
661 | | double frameRate = 0, bitrate = 0; |
662 | | EB_H265_ENC_CONFIGURATION *svtParam = (EB_H265_ENC_CONFIGURATION*)encoder->m_svtAppData->svtHevcParams; |
663 | | if (svtParam->frameRateNumerator && svtParam->frameRateDenominator && (svtParam->frameRateNumerator != 0 && svtParam->frameRateDenominator != 0)) |
664 | | { |
665 | | frameRate = ((double)svtParam->frameRateNumerator) / ((double)svtParam->frameRateDenominator); |
666 | | if(encoder->m_svtAppData->outFrameCount) |
667 | | bitrate = ((double)(encoder->m_svtAppData->byteCount << 3) * frameRate / (encoder->m_svtAppData->outFrameCount * 1000)); |
668 | | |
669 | | printf("Total Frames\t\tFrame Rate\t\tByte Count\t\tBitrate\n"); |
670 | | printf("%12d\t\t%4.2f fps\t\t%10.0f\t\t%5.2f kbps\n", (int32_t)encoder->m_svtAppData->outFrameCount, (double)frameRate, (double)encoder->m_svtAppData->byteCount, bitrate); |
671 | | } |
672 | | } |
673 | | #endif |
674 | | |
675 | | void x265_encoder_close(x265_encoder *enc) |
676 | 698 | { |
677 | 698 | if (enc) |
678 | 698 | { |
679 | 698 | Encoder *encoder = static_cast<Encoder*>(enc); |
680 | | |
681 | | #ifdef SVT_HEVC |
682 | | if (encoder->m_param->bEnableSvtHevc) |
683 | | { |
684 | | EB_ERRORTYPE return_value; |
685 | | return_value = EbDeinitEncoder(encoder->m_svtAppData->svtEncoderHandle); |
686 | | if (return_value != EB_ErrorNone) |
687 | | { |
688 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while closing the encoder \n"); |
689 | | } |
690 | | return_value = EbDeinitHandle(encoder->m_svtAppData->svtEncoderHandle); |
691 | | if (return_value != EB_ErrorNone) |
692 | | { |
693 | | x265_log(encoder->m_param, X265_LOG_ERROR, "SVT HEVC encoder: Error while closing the Handle \n"); |
694 | | } |
695 | | |
696 | | svt_print_summary(enc); |
697 | | EB_H265_ENC_INPUT *inputData = (EB_H265_ENC_INPUT*)encoder->m_svtAppData->inputPictureBuffer->pBuffer; |
698 | | if (inputData->dolbyVisionRpu.payload) X265_FREE(inputData->dolbyVisionRpu.payload); |
699 | | |
700 | | X265_FREE(inputData); |
701 | | X265_FREE(encoder->m_svtAppData->inputPictureBuffer); |
702 | | X265_FREE(encoder->m_svtAppData->svtHevcParams); |
703 | | encoder->stopJobs(); |
704 | | encoder->destroy(); |
705 | | delete encoder; |
706 | | return; |
707 | | } |
708 | | #endif |
709 | | |
710 | 698 | encoder->stopJobs(); |
711 | 698 | encoder->printSummary(); |
712 | 698 | encoder->destroy(); |
713 | 698 | delete encoder; |
714 | 698 | } |
715 | 698 | } |
716 | | |
717 | | int x265_encoder_intra_refresh(x265_encoder *enc) |
718 | 0 | { |
719 | 0 | if (!enc) |
720 | 0 | return -1; |
721 | | |
722 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
723 | 0 | encoder->m_bQueuedIntraRefresh = 1; |
724 | 0 | return 0; |
725 | 0 | } |
726 | | int x265_encoder_ctu_info(x265_encoder *enc, int poc, x265_ctu_info_t** ctu) |
727 | 0 | { |
728 | 0 | if (!ctu || !enc) |
729 | 0 | return -1; |
730 | 0 | Encoder* encoder = static_cast<Encoder*>(enc); |
731 | 0 | encoder->copyCtuInfo(ctu, poc); |
732 | 0 | return 0; |
733 | 0 | } |
734 | | |
735 | | int x265_get_slicetype_poc_and_scenecut(x265_encoder *enc, int *slicetype, int *poc, int *sceneCut) |
736 | 0 | { |
737 | 0 | if (!enc) |
738 | 0 | return -1; |
739 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
740 | 0 | if (!encoder->copySlicetypePocAndSceneCut(slicetype, poc, sceneCut)) |
741 | 0 | return 0; |
742 | 0 | return -1; |
743 | 0 | } |
744 | | |
745 | | int x265_get_ref_frame_list(x265_encoder *enc, x265_picyuv** l0, x265_picyuv** l1, int sliceType, int poc, int* pocL0, int* pocL1) |
746 | 0 | { |
747 | 0 | if (!enc) |
748 | 0 | return -1; |
749 | | |
750 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
751 | 0 | return encoder->getRefFrameList((PicYuv**)l0, (PicYuv**)l1, sliceType, poc, pocL0, pocL1); |
752 | 0 | } |
753 | | |
754 | | int x265_set_analysis_data(x265_encoder *enc, x265_analysis_data *analysis_data, int poc, uint32_t cuBytes) |
755 | 0 | { |
756 | 0 | if (!enc) |
757 | 0 | return -1; |
758 | | |
759 | 0 | Encoder *encoder = static_cast<Encoder*>(enc); |
760 | 0 | if (!encoder->setAnalysisData(analysis_data, poc, cuBytes)) |
761 | 0 | return 0; |
762 | | |
763 | 0 | return -1; |
764 | 0 | } |
765 | | |
766 | | void x265_alloc_analysis_data(x265_param *param, x265_analysis_data* analysis) |
767 | 0 | { |
768 | 0 | x265_analysis_inter_data *interData = analysis->interData = NULL; |
769 | 0 | x265_analysis_intra_data *intraData = analysis->intraData = NULL; |
770 | 0 | x265_analysis_distortion_data *distortionData = analysis->distortionData = NULL; |
771 | |
|
772 | 0 | bool isVbv = param->rc.vbvMaxBitrate > 0 && param->rc.vbvBufferSize > 0; |
773 | 0 | int numDir = 2; //irrespective of P or B slices set direction as 2 |
774 | 0 | uint32_t numPlanes = param->internalCsp == X265_CSP_I400 ? 1 : 3; |
775 | |
|
776 | 0 | int maxReuseLevel = X265_MAX(param->analysisSaveReuseLevel, param->analysisLoadReuseLevel); |
777 | 0 | int minReuseLevel = (param->analysisSaveReuseLevel && param->analysisLoadReuseLevel) ? |
778 | 0 | X265_MIN(param->analysisSaveReuseLevel, param->analysisLoadReuseLevel) : maxReuseLevel; |
779 | |
|
780 | 0 | bool isMultiPassOpt = param->analysisMultiPassRefine || param->analysisMultiPassDistortion; |
781 | | |
782 | | #if X265_DEPTH < 10 && (LINKED_10BIT || LINKED_12BIT) |
783 | | uint32_t numCUs_sse_t = param->internalBitDepth > 8 ? analysis->numCUsInFrame << 1 : analysis->numCUsInFrame; |
784 | | #elif X265_DEPTH >= 10 && LINKED_8BIT |
785 | | uint32_t numCUs_sse_t = param->internalBitDepth > 8 ? analysis->numCUsInFrame : (analysis->numCUsInFrame + 1U) >> 1; |
786 | | #else |
787 | 0 | uint32_t numCUs_sse_t = analysis->numCUsInFrame; |
788 | 0 | #endif |
789 | 0 | if (isMultiPassOpt || param->ctuDistortionRefine) |
790 | 0 | { |
791 | | //Allocate memory for distortionData pointer |
792 | 0 | CHECKED_MALLOC_ZERO(distortionData, x265_analysis_distortion_data, 1); |
793 | 0 | CHECKED_MALLOC_ZERO(distortionData->ctuDistortion, sse_t, analysis->numPartitions * numCUs_sse_t); |
794 | 0 | if (param->analysisLoad || param->rc.bStatRead) |
795 | 0 | { |
796 | 0 | CHECKED_MALLOC_ZERO(distortionData->scaledDistortion, double, analysis->numCUsInFrame); |
797 | 0 | CHECKED_MALLOC_ZERO(distortionData->offset, double, analysis->numCUsInFrame); |
798 | 0 | CHECKED_MALLOC_ZERO(distortionData->threshold, double, analysis->numCUsInFrame); |
799 | 0 | } |
800 | 0 | analysis->distortionData = distortionData; |
801 | 0 | } |
802 | | |
803 | 0 | if (!isMultiPassOpt && param->bDisableLookahead && isVbv) |
804 | 0 | { |
805 | 0 | CHECKED_MALLOC_ZERO(analysis->lookahead.intraSatdForVbv, uint32_t, analysis->numCuInHeight); |
806 | 0 | CHECKED_MALLOC_ZERO(analysis->lookahead.satdForVbv, uint32_t, analysis->numCuInHeight); |
807 | 0 | CHECKED_MALLOC_ZERO(analysis->lookahead.intraVbvCost, uint32_t, analysis->numCUsInFrame); |
808 | 0 | CHECKED_MALLOC_ZERO(analysis->lookahead.vbvCost, uint32_t, analysis->numCUsInFrame); |
809 | 0 | } |
810 | | |
811 | | //Allocate memory for weightParam pointer |
812 | 0 | if (!isMultiPassOpt && !(param->bAnalysisType == AVC_INFO)) |
813 | 0 | CHECKED_MALLOC_ZERO(analysis->wt, x265_weight_param, numPlanes * numDir); |
814 | | |
815 | | //Allocate memory for intraData pointer |
816 | 0 | if ((maxReuseLevel > 1) || isMultiPassOpt) |
817 | 0 | { |
818 | 0 | CHECKED_MALLOC_ZERO(intraData, x265_analysis_intra_data, 1); |
819 | 0 | CHECKED_MALLOC(intraData->depth, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
820 | 0 | } |
821 | | |
822 | 0 | if (maxReuseLevel > 1) |
823 | 0 | { |
824 | 0 | CHECKED_MALLOC_ZERO(intraData->modes, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
825 | 0 | CHECKED_MALLOC_ZERO(intraData->partSizes, char, analysis->numPartitions * analysis->numCUsInFrame); |
826 | 0 | CHECKED_MALLOC_ZERO(intraData->chromaModes, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
827 | 0 | if (param->rc.cuTree) |
828 | 0 | CHECKED_MALLOC_ZERO(intraData->cuQPOff, int8_t, analysis->numPartitions * analysis->numCUsInFrame); |
829 | 0 | } |
830 | 0 | analysis->intraData = intraData; |
831 | |
|
832 | 0 | if ((maxReuseLevel > 1) || isMultiPassOpt) |
833 | 0 | { |
834 | | //Allocate memory for interData pointer based on ReuseLevels |
835 | 0 | CHECKED_MALLOC_ZERO(interData, x265_analysis_inter_data, 1); |
836 | 0 | CHECKED_MALLOC(interData->depth, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
837 | 0 | CHECKED_MALLOC_ZERO(interData->modes, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
838 | |
|
839 | 0 | if (param->rc.cuTree && !isMultiPassOpt) |
840 | 0 | CHECKED_MALLOC_ZERO(interData->cuQPOff, int8_t, analysis->numPartitions * analysis->numCUsInFrame); |
841 | 0 | CHECKED_MALLOC_ZERO(interData->mvpIdx[0], uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
842 | 0 | CHECKED_MALLOC_ZERO(interData->mvpIdx[1], uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
843 | 0 | CHECKED_MALLOC_ZERO(interData->mv[0], x265_analysis_MV, analysis->numPartitions * analysis->numCUsInFrame); |
844 | 0 | CHECKED_MALLOC_ZERO(interData->mv[1], x265_analysis_MV, analysis->numPartitions * analysis->numCUsInFrame); |
845 | 0 | } |
846 | | |
847 | 0 | if (maxReuseLevel > 4) |
848 | 0 | { |
849 | 0 | CHECKED_MALLOC_ZERO(interData->partSize, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
850 | 0 | CHECKED_MALLOC_ZERO(interData->mergeFlag, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
851 | 0 | } |
852 | 0 | if (maxReuseLevel >= 7) |
853 | 0 | { |
854 | 0 | CHECKED_MALLOC_ZERO(interData->interDir, uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
855 | 0 | CHECKED_MALLOC_ZERO(interData->sadCost, int64_t, analysis->numPartitions * analysis->numCUsInFrame); |
856 | 0 | for (int dir = 0; dir < numDir; dir++) |
857 | 0 | { |
858 | 0 | CHECKED_MALLOC_ZERO(interData->refIdx[dir], int8_t, analysis->numPartitions * analysis->numCUsInFrame); |
859 | 0 | CHECKED_MALLOC_ZERO(analysis->modeFlag[dir], uint8_t, analysis->numPartitions * analysis->numCUsInFrame); |
860 | 0 | } |
861 | 0 | } |
862 | 0 | if ((minReuseLevel >= 2) && (minReuseLevel <= 6)) |
863 | 0 | { |
864 | 0 | CHECKED_MALLOC_ZERO(interData->ref, int32_t, analysis->numCUsInFrame * X265_MAX_PRED_MODE_PER_CTU * numDir); |
865 | 0 | } |
866 | 0 | if (isMultiPassOpt) |
867 | 0 | CHECKED_MALLOC_ZERO(interData->ref, int32_t, 2 * analysis->numPartitions * analysis->numCUsInFrame); |
868 | |
|
869 | 0 | analysis->interData = interData; |
870 | |
|
871 | 0 | return; |
872 | | |
873 | 0 | fail: |
874 | 0 | x265_free_analysis_data(param, analysis); |
875 | 0 | } |
876 | | |
877 | | void x265_free_analysis_data(x265_param *param, x265_analysis_data* analysis) |
878 | 0 | { |
879 | 0 | int maxReuseLevel = X265_MAX(param->analysisSaveReuseLevel, param->analysisLoadReuseLevel); |
880 | 0 | int minReuseLevel = (param->analysisSaveReuseLevel && param->analysisLoadReuseLevel) ? |
881 | 0 | X265_MIN(param->analysisSaveReuseLevel, param->analysisLoadReuseLevel) : maxReuseLevel; |
882 | |
|
883 | 0 | bool isVbv = param->rc.vbvMaxBitrate > 0 && param->rc.vbvBufferSize > 0; |
884 | 0 | bool isMultiPassOpt = param->analysisMultiPassRefine || param->analysisMultiPassDistortion; |
885 | | |
886 | | //Free memory for Lookahead pointers |
887 | 0 | if (!isMultiPassOpt && param->bDisableLookahead && isVbv) |
888 | 0 | { |
889 | 0 | X265_FREE(analysis->lookahead.satdForVbv); |
890 | 0 | X265_FREE(analysis->lookahead.intraSatdForVbv); |
891 | 0 | X265_FREE(analysis->lookahead.vbvCost); |
892 | 0 | X265_FREE(analysis->lookahead.intraVbvCost); |
893 | 0 | } |
894 | | |
895 | | //Free memory for distortionData pointers |
896 | 0 | if (analysis->distortionData) |
897 | 0 | { |
898 | 0 | X265_FREE((analysis->distortionData)->ctuDistortion); |
899 | 0 | if (param->rc.bStatRead || param->analysisLoad) |
900 | 0 | { |
901 | 0 | X265_FREE((analysis->distortionData)->scaledDistortion); |
902 | 0 | X265_FREE((analysis->distortionData)->offset); |
903 | 0 | X265_FREE((analysis->distortionData)->threshold); |
904 | 0 | } |
905 | 0 | X265_FREE(analysis->distortionData); |
906 | 0 | } |
907 | | |
908 | | /* Early exit freeing weights alone if level is 1 (when there is no analysis inter/intra) */ |
909 | 0 | if (!isMultiPassOpt && analysis->wt && !(param->bAnalysisType == AVC_INFO)) |
910 | 0 | X265_FREE(analysis->wt); |
911 | | |
912 | | //Free memory for intraData pointers |
913 | 0 | if (analysis->intraData) |
914 | 0 | { |
915 | 0 | X265_FREE((analysis->intraData)->depth); |
916 | 0 | if (!isMultiPassOpt) |
917 | 0 | { |
918 | 0 | X265_FREE((analysis->intraData)->modes); |
919 | 0 | X265_FREE((analysis->intraData)->partSizes); |
920 | 0 | X265_FREE((analysis->intraData)->chromaModes); |
921 | 0 | if (param->rc.cuTree) |
922 | 0 | X265_FREE((analysis->intraData)->cuQPOff); |
923 | 0 | } |
924 | 0 | X265_FREE(analysis->intraData); |
925 | 0 | analysis->intraData = NULL; |
926 | 0 | } |
927 | | |
928 | | //Free interData pointers |
929 | 0 | if (analysis->interData) |
930 | 0 | { |
931 | 0 | X265_FREE((analysis->interData)->depth); |
932 | 0 | X265_FREE((analysis->interData)->modes); |
933 | 0 | if (!isMultiPassOpt && param->rc.cuTree) |
934 | 0 | X265_FREE((analysis->interData)->cuQPOff); |
935 | 0 | X265_FREE((analysis->interData)->mvpIdx[0]); |
936 | 0 | X265_FREE((analysis->interData)->mvpIdx[1]); |
937 | 0 | X265_FREE((analysis->interData)->mv[0]); |
938 | 0 | X265_FREE((analysis->interData)->mv[1]); |
939 | |
|
940 | 0 | if (maxReuseLevel > 4) |
941 | 0 | { |
942 | 0 | X265_FREE((analysis->interData)->mergeFlag); |
943 | 0 | X265_FREE((analysis->interData)->partSize); |
944 | 0 | } |
945 | 0 | if (maxReuseLevel >= 7) |
946 | 0 | { |
947 | 0 | int numDir = 2; |
948 | 0 | X265_FREE((analysis->interData)->interDir); |
949 | 0 | X265_FREE((analysis->interData)->sadCost); |
950 | 0 | for (int dir = 0; dir < numDir; dir++) |
951 | 0 | { |
952 | 0 | X265_FREE((analysis->interData)->refIdx[dir]); |
953 | 0 | if (analysis->modeFlag[dir] != NULL) |
954 | 0 | { |
955 | 0 | X265_FREE(analysis->modeFlag[dir]); |
956 | 0 | analysis->modeFlag[dir] = NULL; |
957 | 0 | } |
958 | 0 | } |
959 | 0 | } |
960 | 0 | if (((minReuseLevel >= 2) && (minReuseLevel <= 6)) || isMultiPassOpt) |
961 | 0 | X265_FREE((analysis->interData)->ref); |
962 | 0 | X265_FREE(analysis->interData); |
963 | 0 | analysis->interData = NULL; |
964 | 0 | } |
965 | 0 | } |
966 | | |
967 | | void x265_cleanup(void) |
968 | 0 | { |
969 | 0 | BitCost::destroy(); |
970 | 0 | } |
971 | | |
972 | | x265_picture *x265_picture_alloc() |
973 | 698 | { |
974 | 698 | return (x265_picture*)x265_malloc(sizeof(x265_picture)); |
975 | 698 | } |
976 | | |
977 | | void x265_picture_init(x265_param *param, x265_picture *pic) |
978 | 698 | { |
979 | 698 | memset(pic, 0, sizeof(x265_picture)); |
980 | | |
981 | 698 | pic->bitDepth = param->internalBitDepth; |
982 | 698 | pic->colorSpace = param->internalCsp; |
983 | 698 | pic->forceqp = X265_QP_AUTO; |
984 | 698 | pic->quantOffsets = NULL; |
985 | 698 | pic->userSEI.payloads = NULL; |
986 | 698 | pic->userSEI.numPayloads = 0; |
987 | 698 | pic->rpu.payloadSize = 0; |
988 | 698 | pic->rpu.payload = NULL; |
989 | 698 | pic->picStruct = 0; |
990 | | |
991 | 698 | if ((param->analysisSave || param->analysisLoad) || (param->bAnalysisType == AVC_INFO)) |
992 | 0 | { |
993 | 0 | uint32_t widthInCU = (param->sourceWidth + param->maxCUSize - 1) >> param->maxLog2CUSize; |
994 | 0 | uint32_t heightInCU = (param->sourceHeight + param->maxCUSize - 1) >> param->maxLog2CUSize; |
995 | |
|
996 | 0 | uint32_t numCUsInFrame = widthInCU * heightInCU; |
997 | 0 | pic->analysisData.numCUsInFrame = numCUsInFrame; |
998 | 0 | pic->analysisData.numPartitions = param->num4x4Partitions; |
999 | 0 | } |
1000 | 698 | } |
1001 | | |
1002 | | void x265_picture_free(x265_picture *p) |
1003 | 698 | { |
1004 | 698 | return x265_free(p); |
1005 | 698 | } |
1006 | | |
1007 | | x265_zone *x265_zone_alloc(int zoneCount, int isZoneFile) |
1008 | 0 | { |
1009 | 0 | x265_zone* zone = (x265_zone*)x265_malloc(sizeof(x265_zone) * zoneCount); |
1010 | 0 | if (isZoneFile) { |
1011 | 0 | for (int i = 0; i < zoneCount; i++) |
1012 | 0 | zone[i].zoneParam = (x265_param*)x265_malloc(sizeof(x265_param)); |
1013 | 0 | } |
1014 | 0 | return zone; |
1015 | 0 | } |
1016 | | |
1017 | | void x265_zone_free(x265_param *param) |
1018 | 2.09k | { |
1019 | 2.09k | if (param && param->rc.zones && (param->rc.zoneCount || param->rc.zonefileCount)) |
1020 | 0 | { |
1021 | 0 | for (int i = 0; i < param->rc.zonefileCount; i++) |
1022 | 0 | x265_free(param->rc.zones[i].zoneParam); |
1023 | 0 | x265_free(param->rc.zones); |
1024 | 0 | } |
1025 | 2.09k | } |
1026 | | |
1027 | | static const x265_api libapi = |
1028 | | { |
1029 | | X265_MAJOR_VERSION, |
1030 | | X265_BUILD, |
1031 | | sizeof(x265_param), |
1032 | | sizeof(x265_picture), |
1033 | | sizeof(x265_analysis_data), |
1034 | | sizeof(x265_zone), |
1035 | | sizeof(x265_stats), |
1036 | | |
1037 | | PFX(max_bit_depth), |
1038 | | PFX(version_str), |
1039 | | PFX(build_info_str), |
1040 | | |
1041 | | &PARAM_NS::x265_param_alloc, |
1042 | | &PARAM_NS::x265_param_free, |
1043 | | &PARAM_NS::x265_param_default, |
1044 | | &PARAM_NS::x265_param_parse, |
1045 | | &PARAM_NS::x265_param_apply_profile, |
1046 | | &PARAM_NS::x265_param_default_preset, |
1047 | | &x265_picture_alloc, |
1048 | | &x265_picture_free, |
1049 | | &x265_picture_init, |
1050 | | &x265_encoder_open, |
1051 | | &x265_encoder_parameters, |
1052 | | &x265_encoder_reconfig, |
1053 | | &x265_encoder_reconfig_zone, |
1054 | | &x265_encoder_headers, |
1055 | | &x265_encoder_encode, |
1056 | | &x265_encoder_get_stats, |
1057 | | &x265_encoder_log, |
1058 | | &x265_encoder_close, |
1059 | | &x265_cleanup, |
1060 | | |
1061 | | sizeof(x265_frame_stats), |
1062 | | &x265_encoder_intra_refresh, |
1063 | | &x265_encoder_ctu_info, |
1064 | | &x265_get_slicetype_poc_and_scenecut, |
1065 | | &x265_get_ref_frame_list, |
1066 | | &x265_csvlog_open, |
1067 | | &x265_csvlog_frame, |
1068 | | &x265_csvlog_encode, |
1069 | | &x265_dither_image, |
1070 | | &x265_set_analysis_data, |
1071 | | #if ENABLE_LIBVMAF |
1072 | | &x265_calculate_vmafscore, |
1073 | | &x265_calculate_vmaf_framelevelscore, |
1074 | | &x265_vmaf_encoder_log, |
1075 | | #endif |
1076 | | &PARAM_NS::x265_zone_param_parse |
1077 | | }; |
1078 | | |
1079 | | typedef const x265_api* (*api_get_func)(int bitDepth); |
1080 | | typedef const x265_api* (*api_query_func)(int bitDepth, int apiVersion, int* err); |
1081 | | |
1082 | 0 | #define xstr(s) str(s) |
1083 | 0 | #define str(s) #s |
1084 | | |
1085 | | #if _WIN32 |
1086 | | #define ext ".dll" |
1087 | | #elif MACOS |
1088 | | #include <dlfcn.h> |
1089 | | #define ext ".dylib" |
1090 | | #else |
1091 | | #include <dlfcn.h> |
1092 | 0 | #define ext ".so" |
1093 | | #endif |
1094 | | #if defined(__GNUC__) && __GNUC__ >= 8 |
1095 | | #pragma GCC diagnostic ignored "-Wcast-function-type" |
1096 | | #endif |
1097 | | |
1098 | | static int g_recursion /* = 0 */; |
1099 | | const x265_api* x265_api_get(int bitDepth) |
1100 | 4.88k | { |
1101 | 4.88k | if (bitDepth && bitDepth != X265_DEPTH) |
1102 | 0 | { |
1103 | | #if LINKED_8BIT |
1104 | | if (bitDepth == 8) return x265_8bit::x265_api_get(0); |
1105 | | #endif |
1106 | | #if LINKED_10BIT |
1107 | | if (bitDepth == 10) return x265_10bit::x265_api_get(0); |
1108 | | #endif |
1109 | | #if LINKED_12BIT |
1110 | | if (bitDepth == 12) return x265_12bit::x265_api_get(0); |
1111 | | #endif |
1112 | |
|
1113 | 0 | const char* libname = NULL; |
1114 | 0 | const char* method = "x265_api_get_" xstr(X265_BUILD); |
1115 | 0 | const char* multilibname = "libx265" ext; |
1116 | |
|
1117 | 0 | if (bitDepth == 12) |
1118 | 0 | libname = "libx265_main12" ext; |
1119 | 0 | else if (bitDepth == 10) |
1120 | 0 | libname = "libx265_main10" ext; |
1121 | 0 | else if (bitDepth == 8) |
1122 | 0 | libname = "libx265_main" ext; |
1123 | 0 | else |
1124 | 0 | return NULL; |
1125 | | |
1126 | 0 | const x265_api* api = NULL; |
1127 | 0 | int reqDepth = 0; |
1128 | |
|
1129 | 0 | if (g_recursion > 1) |
1130 | 0 | return NULL; |
1131 | 0 | else |
1132 | 0 | g_recursion++; |
1133 | | |
1134 | | #if _WIN32 |
1135 | | HMODULE h = LoadLibraryA(libname); |
1136 | | if (!h) |
1137 | | { |
1138 | | h = LoadLibraryA(multilibname); |
1139 | | reqDepth = bitDepth; |
1140 | | } |
1141 | | if (h) |
1142 | | { |
1143 | | api_get_func get = (api_get_func)GetProcAddress(h, method); |
1144 | | if (get) |
1145 | | api = get(reqDepth); |
1146 | | } |
1147 | | #else |
1148 | 0 | void* h = dlopen(libname, RTLD_LAZY | RTLD_LOCAL); |
1149 | 0 | if (!h) |
1150 | 0 | { |
1151 | 0 | h = dlopen(multilibname, RTLD_LAZY | RTLD_LOCAL); |
1152 | 0 | reqDepth = bitDepth; |
1153 | 0 | } |
1154 | 0 | if (h) |
1155 | 0 | { |
1156 | 0 | api_get_func get = (api_get_func)dlsym(h, method); |
1157 | 0 | if (get) |
1158 | 0 | api = get(reqDepth); |
1159 | 0 | } |
1160 | 0 | #endif |
1161 | |
|
1162 | 0 | g_recursion--; |
1163 | |
|
1164 | 0 | if (api && bitDepth != api->bit_depth) |
1165 | 0 | { |
1166 | 0 | x265_log(NULL, X265_LOG_WARNING, "%s does not support requested bitDepth %d\n", libname, bitDepth); |
1167 | 0 | return NULL; |
1168 | 0 | } |
1169 | | |
1170 | 0 | return api; |
1171 | 0 | } |
1172 | | |
1173 | 4.88k | return &libapi; |
1174 | 4.88k | } |
1175 | | |
1176 | | const x265_api* x265_api_query(int bitDepth, int apiVersion, int* err) |
1177 | 0 | { |
1178 | 0 | if (apiVersion < 51) |
1179 | 0 | { |
1180 | | /* builds before 1.6 had re-ordered public structs */ |
1181 | 0 | if (err) *err = X265_API_QUERY_ERR_VER_REFUSED; |
1182 | 0 | return NULL; |
1183 | 0 | } |
1184 | | |
1185 | 0 | if (err) *err = X265_API_QUERY_ERR_NONE; |
1186 | |
|
1187 | 0 | if (bitDepth && bitDepth != X265_DEPTH) |
1188 | 0 | { |
1189 | | #if LINKED_8BIT |
1190 | | if (bitDepth == 8) return x265_8bit::x265_api_query(0, apiVersion, err); |
1191 | | #endif |
1192 | | #if LINKED_10BIT |
1193 | | if (bitDepth == 10) return x265_10bit::x265_api_query(0, apiVersion, err); |
1194 | | #endif |
1195 | | #if LINKED_12BIT |
1196 | | if (bitDepth == 12) return x265_12bit::x265_api_query(0, apiVersion, err); |
1197 | | #endif |
1198 | |
|
1199 | 0 | const char* libname = NULL; |
1200 | 0 | const char* method = "x265_api_query"; |
1201 | 0 | const char* multilibname = "libx265" ext; |
1202 | |
|
1203 | 0 | if (bitDepth == 12) |
1204 | 0 | libname = "libx265_main12" ext; |
1205 | 0 | else if (bitDepth == 10) |
1206 | 0 | libname = "libx265_main10" ext; |
1207 | 0 | else if (bitDepth == 8) |
1208 | 0 | libname = "libx265_main" ext; |
1209 | 0 | else |
1210 | 0 | { |
1211 | 0 | if (err) *err = X265_API_QUERY_ERR_LIB_NOT_FOUND; |
1212 | 0 | return NULL; |
1213 | 0 | } |
1214 | | |
1215 | 0 | const x265_api* api = NULL; |
1216 | 0 | int reqDepth = 0; |
1217 | 0 | int e = X265_API_QUERY_ERR_LIB_NOT_FOUND; |
1218 | |
|
1219 | 0 | if (g_recursion > 1) |
1220 | 0 | { |
1221 | 0 | if (err) *err = X265_API_QUERY_ERR_LIB_NOT_FOUND; |
1222 | 0 | return NULL; |
1223 | 0 | } |
1224 | 0 | else |
1225 | 0 | g_recursion++; |
1226 | | |
1227 | | #if _WIN32 |
1228 | | HMODULE h = LoadLibraryA(libname); |
1229 | | if (!h) |
1230 | | { |
1231 | | h = LoadLibraryA(multilibname); |
1232 | | reqDepth = bitDepth; |
1233 | | } |
1234 | | if (h) |
1235 | | { |
1236 | | e = X265_API_QUERY_ERR_FUNC_NOT_FOUND; |
1237 | | api_query_func query = (api_query_func)GetProcAddress(h, method); |
1238 | | if (query) |
1239 | | api = query(reqDepth, apiVersion, err); |
1240 | | } |
1241 | | #else |
1242 | 0 | void* h = dlopen(libname, RTLD_LAZY | RTLD_LOCAL); |
1243 | 0 | if (!h) |
1244 | 0 | { |
1245 | 0 | h = dlopen(multilibname, RTLD_LAZY | RTLD_LOCAL); |
1246 | 0 | reqDepth = bitDepth; |
1247 | 0 | } |
1248 | 0 | if (h) |
1249 | 0 | { |
1250 | 0 | e = X265_API_QUERY_ERR_FUNC_NOT_FOUND; |
1251 | 0 | api_query_func query = (api_query_func)dlsym(h, method); |
1252 | 0 | if (query) |
1253 | 0 | api = query(reqDepth, apiVersion, err); |
1254 | 0 | } |
1255 | 0 | #endif |
1256 | |
|
1257 | 0 | g_recursion--; |
1258 | |
|
1259 | 0 | if (api && bitDepth != api->bit_depth) |
1260 | 0 | { |
1261 | 0 | x265_log(NULL, X265_LOG_WARNING, "%s does not support requested bitDepth %d\n", libname, bitDepth); |
1262 | 0 | if (err) *err = X265_API_QUERY_ERR_WRONG_BITDEPTH; |
1263 | 0 | return NULL; |
1264 | 0 | } |
1265 | | |
1266 | 0 | if (err) *err = api ? X265_API_QUERY_ERR_NONE : e; |
1267 | 0 | return api; |
1268 | 0 | } |
1269 | | |
1270 | 0 | return &libapi; |
1271 | 0 | } |
1272 | | |
1273 | | FILE* x265_csvlog_open(const x265_param* param) |
1274 | 0 | { |
1275 | 0 | FILE *csvfp = x265_fopen(param->csvfn, "r"); |
1276 | 0 | if (csvfp) |
1277 | 0 | { |
1278 | | /* file already exists, re-open for append */ |
1279 | 0 | fclose(csvfp); |
1280 | 0 | return x265_fopen(param->csvfn, "ab"); |
1281 | 0 | } |
1282 | 0 | else |
1283 | 0 | { |
1284 | | /* new CSV file, write header */ |
1285 | 0 | csvfp = x265_fopen(param->csvfn, "wb"); |
1286 | 0 | if (csvfp) |
1287 | 0 | { |
1288 | 0 | if (param->csvLogLevel) |
1289 | 0 | { |
1290 | 0 | fprintf(csvfp, "Encode Order, Type, POC, QP, Bits, Scenecut, "); |
1291 | 0 | if (param->csvLogLevel >= 2) |
1292 | 0 | fprintf(csvfp, "I/P cost ratio, "); |
1293 | 0 | if (param->rc.rateControlMode == X265_RC_CRF) |
1294 | 0 | fprintf(csvfp, "RateFactor, "); |
1295 | 0 | if (param->rc.vbvBufferSize) |
1296 | 0 | fprintf(csvfp, "BufferFill, BufferFillFinal, "); |
1297 | 0 | if (param->rc.vbvBufferSize && param->csvLogLevel >= 2) |
1298 | 0 | fprintf(csvfp, "UnclippedBufferFillFinal, "); |
1299 | 0 | if (param->bEnablePsnr) |
1300 | 0 | fprintf(csvfp, "Y PSNR, U PSNR, V PSNR, YUV PSNR, "); |
1301 | 0 | if (param->bEnableSsim) |
1302 | 0 | fprintf(csvfp, "SSIM, SSIM(dB), "); |
1303 | 0 | fprintf(csvfp, "Latency, "); |
1304 | 0 | fprintf(csvfp, "List 0, List 1"); |
1305 | 0 | uint32_t size = param->maxCUSize; |
1306 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1307 | 0 | { |
1308 | 0 | fprintf(csvfp, ", Intra %dx%d DC, Intra %dx%d Planar, Intra %dx%d Ang", size, size, size, size, size, size); |
1309 | 0 | size /= 2; |
1310 | 0 | } |
1311 | 0 | fprintf(csvfp, ", 4x4"); |
1312 | 0 | size = param->maxCUSize; |
1313 | 0 | if (param->bEnableRectInter) |
1314 | 0 | { |
1315 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1316 | 0 | { |
1317 | 0 | fprintf(csvfp, ", Inter %dx%d, Inter %dx%d (Rect)", size, size, size, size); |
1318 | 0 | if (param->bEnableAMP) |
1319 | 0 | fprintf(csvfp, ", Inter %dx%d (Amp)", size, size); |
1320 | 0 | size /= 2; |
1321 | 0 | } |
1322 | 0 | } |
1323 | 0 | else |
1324 | 0 | { |
1325 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1326 | 0 | { |
1327 | 0 | fprintf(csvfp, ", Inter %dx%d", size, size); |
1328 | 0 | size /= 2; |
1329 | 0 | } |
1330 | 0 | } |
1331 | 0 | size = param->maxCUSize; |
1332 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1333 | 0 | { |
1334 | 0 | fprintf(csvfp, ", Skip %dx%d", size, size); |
1335 | 0 | size /= 2; |
1336 | 0 | } |
1337 | 0 | size = param->maxCUSize; |
1338 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1339 | 0 | { |
1340 | 0 | fprintf(csvfp, ", Merge %dx%d", size, size); |
1341 | 0 | size /= 2; |
1342 | 0 | } |
1343 | |
|
1344 | 0 | if (param->csvLogLevel >= 2) |
1345 | 0 | { |
1346 | 0 | fprintf(csvfp, ", Avg Luma Distortion, Avg Chroma Distortion, Avg psyEnergy, Avg Residual Energy," |
1347 | 0 | " Min Luma Level, Max Luma Level, Avg Luma Level"); |
1348 | |
|
1349 | 0 | if (param->internalCsp != X265_CSP_I400) |
1350 | 0 | fprintf(csvfp, ", Min Cb Level, Max Cb Level, Avg Cb Level, Min Cr Level, Max Cr Level, Avg Cr Level"); |
1351 | | |
1352 | | /* PU statistics */ |
1353 | 0 | size = param->maxCUSize; |
1354 | 0 | for (uint32_t i = 0; i< param->maxLog2CUSize - (uint32_t)g_log2Size[param->minCUSize] + 1; i++) |
1355 | 0 | { |
1356 | 0 | fprintf(csvfp, ", Intra %dx%d", size, size); |
1357 | 0 | fprintf(csvfp, ", Skip %dx%d", size, size); |
1358 | 0 | fprintf(csvfp, ", AMP %d", size); |
1359 | 0 | fprintf(csvfp, ", Inter %dx%d", size, size); |
1360 | 0 | fprintf(csvfp, ", Merge %dx%d", size, size); |
1361 | 0 | fprintf(csvfp, ", Inter %dx%d", size, size / 2); |
1362 | 0 | fprintf(csvfp, ", Merge %dx%d", size, size / 2); |
1363 | 0 | fprintf(csvfp, ", Inter %dx%d", size / 2, size); |
1364 | 0 | fprintf(csvfp, ", Merge %dx%d", size / 2, size); |
1365 | 0 | size /= 2; |
1366 | 0 | } |
1367 | |
|
1368 | 0 | if ((uint32_t)g_log2Size[param->minCUSize] == 3) |
1369 | 0 | fprintf(csvfp, ", 4x4"); |
1370 | | |
1371 | | /* detailed performance statistics */ |
1372 | 0 | fprintf(csvfp, ", DecideWait (ms), Row0Wait (ms), Wall time (ms), Ref Wait Wall (ms), Total CTU time (ms)," |
1373 | 0 | "Stall Time (ms), Total frame time (ms), Avg WPP, Row Blocks"); |
1374 | | #if ENABLE_LIBVMAF |
1375 | | fprintf(csvfp, ", VMAF Frame Score"); |
1376 | | #endif |
1377 | 0 | } |
1378 | 0 | fprintf(csvfp, "\n"); |
1379 | 0 | } |
1380 | 0 | else |
1381 | 0 | { |
1382 | 0 | fputs(summaryCSVHeader, csvfp); |
1383 | 0 | if (param->csvLogLevel >= 2 || param->maxCLL || param->maxFALL) |
1384 | 0 | fputs("MaxCLL, MaxFALL,", csvfp); |
1385 | | #if ENABLE_LIBVMAF |
1386 | | fputs(" Aggregate VMAF Score,", csvfp); |
1387 | | #endif |
1388 | 0 | fputs(" Version\n", csvfp); |
1389 | 0 | } |
1390 | 0 | } |
1391 | 0 | return csvfp; |
1392 | 0 | } |
1393 | 0 | } |
1394 | | |
1395 | | // per frame CSV logging |
1396 | | void x265_csvlog_frame(const x265_param* param, const x265_picture* pic) |
1397 | 0 | { |
1398 | 0 | if (!param->csvfpt) |
1399 | 0 | return; |
1400 | | |
1401 | 0 | const x265_frame_stats* frameStats = &pic->frameData; |
1402 | 0 | fprintf(param->csvfpt, "%d, %c-SLICE, %4d, %2.2lf, %10d, %d,", frameStats->encoderOrder, frameStats->sliceType, frameStats->poc, |
1403 | 0 | frameStats->qp, (int)frameStats->bits, frameStats->bScenecut); |
1404 | 0 | if (param->csvLogLevel >= 2) |
1405 | 0 | fprintf(param->csvfpt, "%.2f,", frameStats->ipCostRatio); |
1406 | 0 | if (param->rc.rateControlMode == X265_RC_CRF) |
1407 | 0 | fprintf(param->csvfpt, "%.3lf,", frameStats->rateFactor); |
1408 | 0 | if (param->rc.vbvBufferSize) |
1409 | 0 | fprintf(param->csvfpt, "%.3lf, %.3lf,", frameStats->bufferFill, frameStats->bufferFillFinal); |
1410 | 0 | if (param->rc.vbvBufferSize && param->csvLogLevel >= 2) |
1411 | 0 | fprintf(param->csvfpt, "%.3lf,", frameStats->unclippedBufferFillFinal); |
1412 | 0 | if (param->bEnablePsnr) |
1413 | 0 | fprintf(param->csvfpt, "%.3lf, %.3lf, %.3lf, %.3lf,", frameStats->psnrY, frameStats->psnrU, frameStats->psnrV, frameStats->psnr); |
1414 | 0 | if (param->bEnableSsim) |
1415 | 0 | fprintf(param->csvfpt, " %.6f, %6.3f,", frameStats->ssim, x265_ssim2dB(frameStats->ssim)); |
1416 | 0 | fprintf(param->csvfpt, "%d, ", frameStats->frameLatency); |
1417 | 0 | if (frameStats->sliceType == 'I' || frameStats->sliceType == 'i') |
1418 | 0 | fputs(" -, -,", param->csvfpt); |
1419 | 0 | else |
1420 | 0 | { |
1421 | 0 | int i = 0; |
1422 | 0 | while (frameStats->list0POC[i] != -1) |
1423 | 0 | fprintf(param->csvfpt, "%d ", frameStats->list0POC[i++]); |
1424 | 0 | fprintf(param->csvfpt, ","); |
1425 | 0 | if (frameStats->sliceType != 'P') |
1426 | 0 | { |
1427 | 0 | i = 0; |
1428 | 0 | while (frameStats->list1POC[i] != -1) |
1429 | 0 | fprintf(param->csvfpt, "%d ", frameStats->list1POC[i++]); |
1430 | 0 | fprintf(param->csvfpt, ","); |
1431 | 0 | } |
1432 | 0 | else |
1433 | 0 | fputs(" -,", param->csvfpt); |
1434 | 0 | } |
1435 | |
|
1436 | 0 | if (param->csvLogLevel) |
1437 | 0 | { |
1438 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1439 | 0 | fprintf(param->csvfpt, "%5.2lf%%, %5.2lf%%, %5.2lf%%,", frameStats->cuStats.percentIntraDistribution[depth][0], |
1440 | 0 | frameStats->cuStats.percentIntraDistribution[depth][1], |
1441 | 0 | frameStats->cuStats.percentIntraDistribution[depth][2]); |
1442 | 0 | fprintf(param->csvfpt, "%5.2lf%%", frameStats->cuStats.percentIntraNxN); |
1443 | 0 | if (param->bEnableRectInter) |
1444 | 0 | { |
1445 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1446 | 0 | { |
1447 | 0 | fprintf(param->csvfpt, ", %5.2lf%%, %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0], |
1448 | 0 | frameStats->cuStats.percentInterDistribution[depth][1]); |
1449 | 0 | if (param->bEnableAMP) |
1450 | 0 | fprintf(param->csvfpt, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][2]); |
1451 | 0 | } |
1452 | 0 | } |
1453 | 0 | else |
1454 | 0 | { |
1455 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1456 | 0 | fprintf(param->csvfpt, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0]); |
1457 | 0 | } |
1458 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1459 | 0 | fprintf(param->csvfpt, ", %5.2lf%%", frameStats->cuStats.percentSkipCu[depth]); |
1460 | 0 | for (uint32_t depth = 0; depth <= param->maxCUDepth; depth++) |
1461 | 0 | fprintf(param->csvfpt, ", %5.2lf%%", frameStats->cuStats.percentMergeCu[depth]); |
1462 | 0 | } |
1463 | |
|
1464 | 0 | if (param->csvLogLevel >= 2) |
1465 | 0 | { |
1466 | 0 | fprintf(param->csvfpt, ", %.2lf, %.2lf, %.2lf, %.2lf ", frameStats->avgLumaDistortion, |
1467 | 0 | frameStats->avgChromaDistortion, |
1468 | 0 | frameStats->avgPsyEnergy, |
1469 | 0 | frameStats->avgResEnergy); |
1470 | |
|
1471 | 0 | fprintf(param->csvfpt, ", %d, %d, %.2lf", frameStats->minLumaLevel, frameStats->maxLumaLevel, frameStats->avgLumaLevel); |
1472 | |
|
1473 | 0 | if (param->internalCsp != X265_CSP_I400) |
1474 | 0 | { |
1475 | 0 | fprintf(param->csvfpt, ", %d, %d, %.2lf", frameStats->minChromaULevel, frameStats->maxChromaULevel, frameStats->avgChromaULevel); |
1476 | 0 | fprintf(param->csvfpt, ", %d, %d, %.2lf", frameStats->minChromaVLevel, frameStats->maxChromaVLevel, frameStats->avgChromaVLevel); |
1477 | 0 | } |
1478 | |
|
1479 | 0 | for (uint32_t i = 0; i < param->maxLog2CUSize - (uint32_t)g_log2Size[param->minCUSize] + 1; i++) |
1480 | 0 | { |
1481 | 0 | fprintf(param->csvfpt, ", %.2lf%%", frameStats->puStats.percentIntraPu[i]); |
1482 | 0 | fprintf(param->csvfpt, ", %.2lf%%", frameStats->puStats.percentSkipPu[i]); |
1483 | 0 | fprintf(param->csvfpt, ",%.2lf%%", frameStats->puStats.percentAmpPu[i]); |
1484 | 0 | for (uint32_t j = 0; j < 3; j++) |
1485 | 0 | { |
1486 | 0 | fprintf(param->csvfpt, ", %.2lf%%", frameStats->puStats.percentInterPu[i][j]); |
1487 | 0 | fprintf(param->csvfpt, ", %.2lf%%", frameStats->puStats.percentMergePu[i][j]); |
1488 | 0 | } |
1489 | 0 | } |
1490 | 0 | if ((uint32_t)g_log2Size[param->minCUSize] == 3) |
1491 | 0 | fprintf(param->csvfpt, ",%.2lf%%", frameStats->puStats.percentNxN); |
1492 | |
|
1493 | 0 | fprintf(param->csvfpt, ", %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf,", frameStats->decideWaitTime, frameStats->row0WaitTime, |
1494 | 0 | frameStats->wallTime, frameStats->refWaitWallTime, |
1495 | 0 | frameStats->totalCTUTime, frameStats->stallTime, |
1496 | 0 | frameStats->totalFrameTime); |
1497 | |
|
1498 | 0 | fprintf(param->csvfpt, " %.3lf, %d", frameStats->avgWPP, frameStats->countRowBlocks); |
1499 | | #if ENABLE_LIBVMAF |
1500 | | fprintf(param->csvfpt, ", %lf", frameStats->vmafFrameScore); |
1501 | | #endif |
1502 | 0 | } |
1503 | 0 | fprintf(param->csvfpt, "\n"); |
1504 | 0 | fflush(stderr); |
1505 | 0 | } |
1506 | | |
1507 | | void x265_csvlog_encode(const x265_param *p, const x265_stats *stats, int padx, int pady, int argc, char** argv) |
1508 | 0 | { |
1509 | 0 | if (p && p->csvfpt) |
1510 | 0 | { |
1511 | 0 | const x265_api * api = x265_api_get(0); |
1512 | |
|
1513 | 0 | if (p->csvLogLevel) |
1514 | 0 | { |
1515 | | // adding summary to a per-frame csv log file, so it needs a summary header |
1516 | 0 | fprintf(p->csvfpt, "\nSummary\n"); |
1517 | 0 | fputs(summaryCSVHeader, p->csvfpt); |
1518 | 0 | if (p->csvLogLevel >= 2 || p->maxCLL || p->maxFALL) |
1519 | 0 | fputs("MaxCLL, MaxFALL,", p->csvfpt); |
1520 | | #if ENABLE_LIBVMAF |
1521 | | fputs(" Aggregate VMAF score,", p->csvfpt); |
1522 | | #endif |
1523 | 0 | fputs(" Version\n",p->csvfpt); |
1524 | |
|
1525 | 0 | } |
1526 | | // CLI arguments or other |
1527 | 0 | if (argc) |
1528 | 0 | { |
1529 | 0 | fputc('"', p->csvfpt); |
1530 | 0 | for (int i = 1; i < argc; i++) |
1531 | 0 | { |
1532 | 0 | fputc(' ', p->csvfpt); |
1533 | 0 | fputs(argv[i], p->csvfpt); |
1534 | 0 | } |
1535 | 0 | fputc('"', p->csvfpt); |
1536 | 0 | } |
1537 | 0 | else |
1538 | 0 | { |
1539 | 0 | char *opts = x265_param2string((x265_param*)p, padx, pady); |
1540 | 0 | if (opts) |
1541 | 0 | { |
1542 | 0 | fputc('"', p->csvfpt); |
1543 | 0 | fputs(opts, p->csvfpt); |
1544 | 0 | fputc('"', p->csvfpt); |
1545 | 0 | X265_FREE(opts); |
1546 | 0 | } |
1547 | 0 | } |
1548 | | |
1549 | | // current date and time |
1550 | 0 | time_t now; |
1551 | 0 | struct tm* timeinfo; |
1552 | 0 | time(&now); |
1553 | 0 | timeinfo = localtime(&now); |
1554 | 0 | char buffer[200]; |
1555 | 0 | strftime(buffer, 128, "%c", timeinfo); |
1556 | 0 | fprintf(p->csvfpt, ", %s, ", buffer); |
1557 | | // elapsed time, fps, bitrate |
1558 | 0 | fprintf(p->csvfpt, "%.2f, %.2f, %.2f,", |
1559 | 0 | stats->elapsedEncodeTime, stats->encodedPictureCount / stats->elapsedEncodeTime, stats->bitrate); |
1560 | |
|
1561 | 0 | if (p->bEnablePsnr) |
1562 | 0 | fprintf(p->csvfpt, " %.3lf, %.3lf, %.3lf, %.3lf,", |
1563 | 0 | stats->globalPsnrY / stats->encodedPictureCount, stats->globalPsnrU / stats->encodedPictureCount, |
1564 | 0 | stats->globalPsnrV / stats->encodedPictureCount, stats->globalPsnr); |
1565 | 0 | else |
1566 | 0 | fprintf(p->csvfpt, " -, -, -, -,"); |
1567 | 0 | if (p->bEnableSsim) |
1568 | 0 | fprintf(p->csvfpt, " %.6f, %6.3f,", stats->globalSsim, x265_ssim2dB(stats->globalSsim)); |
1569 | 0 | else |
1570 | 0 | fprintf(p->csvfpt, " -, -,"); |
1571 | |
|
1572 | 0 | if (stats->statsI.numPics) |
1573 | 0 | { |
1574 | 0 | fprintf(p->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats->statsI.numPics, stats->statsI.avgQp, stats->statsI.bitrate); |
1575 | 0 | if (p->bEnablePsnr) |
1576 | 0 | fprintf(p->csvfpt, " %.3lf, %.3lf, %.3lf,", stats->statsI.psnrY, stats->statsI.psnrU, stats->statsI.psnrV); |
1577 | 0 | else |
1578 | 0 | fprintf(p->csvfpt, " -, -, -,"); |
1579 | 0 | if (p->bEnableSsim) |
1580 | 0 | fprintf(p->csvfpt, " %.3lf,", stats->statsI.ssim); |
1581 | 0 | else |
1582 | 0 | fprintf(p->csvfpt, " -,"); |
1583 | 0 | } |
1584 | 0 | else |
1585 | 0 | fprintf(p->csvfpt, " -, -, -, -, -, -, -,"); |
1586 | |
|
1587 | 0 | if (stats->statsP.numPics) |
1588 | 0 | { |
1589 | 0 | fprintf(p->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats->statsP.numPics, stats->statsP.avgQp, stats->statsP.bitrate); |
1590 | 0 | if (p->bEnablePsnr) |
1591 | 0 | fprintf(p->csvfpt, " %.3lf, %.3lf, %.3lf,", stats->statsP.psnrY, stats->statsP.psnrU, stats->statsP.psnrV); |
1592 | 0 | else |
1593 | 0 | fprintf(p->csvfpt, " -, -, -,"); |
1594 | 0 | if (p->bEnableSsim) |
1595 | 0 | fprintf(p->csvfpt, " %.3lf,", stats->statsP.ssim); |
1596 | 0 | else |
1597 | 0 | fprintf(p->csvfpt, " -,"); |
1598 | 0 | } |
1599 | 0 | else |
1600 | 0 | fprintf(p->csvfpt, " -, -, -, -, -, -, -,"); |
1601 | |
|
1602 | 0 | if (stats->statsB.numPics) |
1603 | 0 | { |
1604 | 0 | fprintf(p->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats->statsB.numPics, stats->statsB.avgQp, stats->statsB.bitrate); |
1605 | 0 | if (p->bEnablePsnr) |
1606 | 0 | fprintf(p->csvfpt, " %.3lf, %.3lf, %.3lf,", stats->statsB.psnrY, stats->statsB.psnrU, stats->statsB.psnrV); |
1607 | 0 | else |
1608 | 0 | fprintf(p->csvfpt, " -, -, -,"); |
1609 | 0 | if (p->bEnableSsim) |
1610 | 0 | fprintf(p->csvfpt, " %.3lf,", stats->statsB.ssim); |
1611 | 0 | else |
1612 | 0 | fprintf(p->csvfpt, " -,"); |
1613 | 0 | } |
1614 | 0 | else |
1615 | 0 | fprintf(p->csvfpt, " -, -, -, -, -, -, -,"); |
1616 | 0 | if (p->csvLogLevel >= 2 || p->maxCLL || p->maxFALL) |
1617 | 0 | fprintf(p->csvfpt, " %-6u, %-6u,", stats->maxCLL, stats->maxFALL); |
1618 | | #if ENABLE_LIBVMAF |
1619 | | fprintf(p->csvfpt, " %lf,", stats->aggregateVmafScore); |
1620 | | #endif |
1621 | 0 | fprintf(p->csvfpt, " %s\n", api->version_str); |
1622 | |
|
1623 | 0 | } |
1624 | 0 | } |
1625 | | |
1626 | | /* The dithering algorithm is based on Sierra-2-4A error diffusion. |
1627 | | * We convert planes in place (without allocating a new buffer). */ |
1628 | | static void ditherPlane(uint16_t *src, int srcStride, int width, int height, int16_t *errors, int bitDepth) |
1629 | 0 | { |
1630 | 0 | const int lShift = 16 - bitDepth; |
1631 | 0 | const int rShift = 16 - bitDepth + 2; |
1632 | 0 | const int half = (1 << (16 - bitDepth + 1)); |
1633 | 0 | const int pixelMax = (1 << bitDepth) - 1; |
1634 | |
|
1635 | 0 | memset(errors, 0, (width + 1) * sizeof(int16_t)); |
1636 | |
|
1637 | 0 | if (bitDepth == 8) |
1638 | 0 | { |
1639 | 0 | for (int y = 0; y < height; y++, src += srcStride) |
1640 | 0 | { |
1641 | 0 | uint8_t* dst = (uint8_t *)src; |
1642 | 0 | int16_t err = 0; |
1643 | 0 | for (int x = 0; x < width; x++) |
1644 | 0 | { |
1645 | 0 | err = err * 2 + errors[x] + errors[x + 1]; |
1646 | 0 | int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); |
1647 | 0 | errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); |
1648 | 0 | dst[x] = (uint8_t)tmpDst; |
1649 | 0 | } |
1650 | 0 | } |
1651 | 0 | } |
1652 | 0 | else |
1653 | 0 | { |
1654 | 0 | for (int y = 0; y < height; y++, src += srcStride) |
1655 | 0 | { |
1656 | 0 | int16_t err = 0; |
1657 | 0 | for (int x = 0; x < width; x++) |
1658 | 0 | { |
1659 | 0 | err = err * 2 + errors[x] + errors[x + 1]; |
1660 | 0 | int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); |
1661 | 0 | errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); |
1662 | 0 | src[x] = (uint16_t)tmpDst; |
1663 | 0 | } |
1664 | 0 | } |
1665 | 0 | } |
1666 | 0 | } |
1667 | | |
1668 | | void x265_dither_image(x265_picture* picIn, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth) |
1669 | 0 | { |
1670 | 0 | const x265_api* api = x265_api_get(0); |
1671 | |
|
1672 | 0 | if (sizeof(x265_picture) != api->sizeof_picture) |
1673 | 0 | { |
1674 | 0 | fprintf(stderr, "extras [error]: structure size skew, unable to dither\n"); |
1675 | 0 | return; |
1676 | 0 | } |
1677 | | |
1678 | 0 | if (picIn->bitDepth <= 8) |
1679 | 0 | { |
1680 | 0 | fprintf(stderr, "extras [error]: dither support enabled only for input bitdepth > 8\n"); |
1681 | 0 | return; |
1682 | 0 | } |
1683 | | |
1684 | 0 | if (picIn->bitDepth == bitDepth) |
1685 | 0 | { |
1686 | 0 | fprintf(stderr, "extras[error]: dither support enabled only if encoder depth is different from picture depth\n"); |
1687 | 0 | return; |
1688 | 0 | } |
1689 | | |
1690 | | /* This portion of code is from readFrame in x264. */ |
1691 | 0 | for (int i = 0; i < x265_cli_csps[picIn->colorSpace].planes; i++) |
1692 | 0 | { |
1693 | 0 | if (picIn->bitDepth < 16) |
1694 | 0 | { |
1695 | | /* upconvert non 16bit high depth planes to 16bit */ |
1696 | 0 | uint16_t *plane = (uint16_t*)picIn->planes[i]; |
1697 | 0 | uint32_t pixelCount = x265_picturePlaneSize(picIn->colorSpace, picWidth, picHeight, i); |
1698 | 0 | int lShift = 16 - picIn->bitDepth; |
1699 | | |
1700 | | /* This loop assumes width is equal to stride which |
1701 | | * happens to be true for file reader outputs */ |
1702 | 0 | for (uint32_t j = 0; j < pixelCount; j++) |
1703 | 0 | plane[j] = plane[j] << lShift; |
1704 | 0 | } |
1705 | |
|
1706 | 0 | int height = (int)(picHeight >> x265_cli_csps[picIn->colorSpace].height[i]); |
1707 | 0 | int width = (int)(picWidth >> x265_cli_csps[picIn->colorSpace].width[i]); |
1708 | |
|
1709 | 0 | ditherPlane(((uint16_t*)picIn->planes[i]), picIn->stride[i] / 2, width, height, errorBuf, bitDepth); |
1710 | 0 | } |
1711 | 0 | } |
1712 | | |
1713 | | #if ENABLE_LIBVMAF |
1714 | | /* Read y values of single frame for 8-bit input */ |
1715 | | int read_image_byte(FILE *file, float *buf, int width, int height, int stride) |
1716 | | { |
1717 | | char *byte_ptr = (char *)buf; |
1718 | | unsigned char *tmp_buf = 0; |
1719 | | int i, j; |
1720 | | int ret = 1; |
1721 | | |
1722 | | if (width <= 0 || height <= 0) |
1723 | | { |
1724 | | goto fail_or_end; |
1725 | | } |
1726 | | |
1727 | | if (!(tmp_buf = (unsigned char*)malloc(width))) |
1728 | | { |
1729 | | goto fail_or_end; |
1730 | | } |
1731 | | |
1732 | | for (i = 0; i < height; ++i) |
1733 | | { |
1734 | | float *row_ptr = (float *)byte_ptr; |
1735 | | |
1736 | | if (fread(tmp_buf, 1, width, file) != (size_t)width) |
1737 | | { |
1738 | | goto fail_or_end; |
1739 | | } |
1740 | | |
1741 | | for (j = 0; j < width; ++j) |
1742 | | { |
1743 | | row_ptr[j] = tmp_buf[j]; |
1744 | | } |
1745 | | |
1746 | | byte_ptr += stride; |
1747 | | } |
1748 | | |
1749 | | ret = 0; |
1750 | | |
1751 | | fail_or_end: |
1752 | | free(tmp_buf); |
1753 | | return ret; |
1754 | | } |
1755 | | /* Read y values of single frame for 10-bit input */ |
1756 | | int read_image_word(FILE *file, float *buf, int width, int height, int stride) |
1757 | | { |
1758 | | char *byte_ptr = (char *)buf; |
1759 | | unsigned short *tmp_buf = 0; |
1760 | | int i, j; |
1761 | | int ret = 1; |
1762 | | |
1763 | | if (width <= 0 || height <= 0) |
1764 | | { |
1765 | | goto fail_or_end; |
1766 | | } |
1767 | | |
1768 | | if (!(tmp_buf = (unsigned short*)malloc(width * 2))) // '*2' to accommodate words |
1769 | | { |
1770 | | goto fail_or_end; |
1771 | | } |
1772 | | |
1773 | | for (i = 0; i < height; ++i) |
1774 | | { |
1775 | | float *row_ptr = (float *)byte_ptr; |
1776 | | |
1777 | | if (fread(tmp_buf, 2, width, file) != (size_t)width) // '2' for word |
1778 | | { |
1779 | | goto fail_or_end; |
1780 | | } |
1781 | | |
1782 | | for (j = 0; j < width; ++j) |
1783 | | { |
1784 | | row_ptr[j] = tmp_buf[j] / 4.0; // '/4' to convert from 10 to 8-bit |
1785 | | } |
1786 | | |
1787 | | byte_ptr += stride; |
1788 | | } |
1789 | | |
1790 | | ret = 0; |
1791 | | |
1792 | | fail_or_end: |
1793 | | free(tmp_buf); |
1794 | | return ret; |
1795 | | } |
1796 | | |
1797 | | int read_frame(float *reference_data, float *distorted_data, float *temp_data, int stride_byte, void *s) |
1798 | | { |
1799 | | x265_vmaf_data *user_data = (x265_vmaf_data *)s; |
1800 | | int ret; |
1801 | | |
1802 | | // read reference y |
1803 | | if (user_data->internalBitDepth == 8) |
1804 | | { |
1805 | | ret = read_image_byte(user_data->reference_file, reference_data, user_data->width, user_data->height, stride_byte); |
1806 | | } |
1807 | | else if (user_data->internalBitDepth == 10) |
1808 | | { |
1809 | | ret = read_image_word(user_data->reference_file, reference_data, user_data->width, user_data->height, stride_byte); |
1810 | | } |
1811 | | else |
1812 | | { |
1813 | | x265_log(NULL, X265_LOG_ERROR, "Invalid bitdepth\n"); |
1814 | | return 1; |
1815 | | } |
1816 | | if (ret) |
1817 | | { |
1818 | | if (feof(user_data->reference_file)) |
1819 | | { |
1820 | | ret = 2; // OK if end of file |
1821 | | } |
1822 | | return ret; |
1823 | | } |
1824 | | |
1825 | | // read distorted y |
1826 | | if (user_data->internalBitDepth == 8) |
1827 | | { |
1828 | | ret = read_image_byte(user_data->distorted_file, distorted_data, user_data->width, user_data->height, stride_byte); |
1829 | | } |
1830 | | else if (user_data->internalBitDepth == 10) |
1831 | | { |
1832 | | ret = read_image_word(user_data->distorted_file, distorted_data, user_data->width, user_data->height, stride_byte); |
1833 | | } |
1834 | | else |
1835 | | { |
1836 | | x265_log(NULL, X265_LOG_ERROR, "Invalid bitdepth\n"); |
1837 | | return 1; |
1838 | | } |
1839 | | if (ret) |
1840 | | { |
1841 | | if (feof(user_data->distorted_file)) |
1842 | | { |
1843 | | ret = 2; // OK if end of file |
1844 | | } |
1845 | | return ret; |
1846 | | } |
1847 | | |
1848 | | // reference skip u and v |
1849 | | if (user_data->internalBitDepth == 8) |
1850 | | { |
1851 | | if (fread(temp_data, 1, user_data->offset, user_data->reference_file) != (size_t)user_data->offset) |
1852 | | { |
1853 | | x265_log(NULL, X265_LOG_ERROR, "reference fread to skip u and v failed.\n"); |
1854 | | goto fail_or_end; |
1855 | | } |
1856 | | } |
1857 | | else if (user_data->internalBitDepth == 10) |
1858 | | { |
1859 | | if (fread(temp_data, 2, user_data->offset, user_data->reference_file) != (size_t)user_data->offset) |
1860 | | { |
1861 | | x265_log(NULL, X265_LOG_ERROR, "reference fread to skip u and v failed.\n"); |
1862 | | goto fail_or_end; |
1863 | | } |
1864 | | } |
1865 | | else |
1866 | | { |
1867 | | x265_log(NULL, X265_LOG_ERROR, "Invalid format\n"); |
1868 | | goto fail_or_end; |
1869 | | } |
1870 | | |
1871 | | // distorted skip u and v |
1872 | | if (user_data->internalBitDepth == 8) |
1873 | | { |
1874 | | if (fread(temp_data, 1, user_data->offset, user_data->distorted_file) != (size_t)user_data->offset) |
1875 | | { |
1876 | | x265_log(NULL, X265_LOG_ERROR, "distorted fread to skip u and v failed.\n"); |
1877 | | goto fail_or_end; |
1878 | | } |
1879 | | } |
1880 | | else if (user_data->internalBitDepth == 10) |
1881 | | { |
1882 | | if (fread(temp_data, 2, user_data->offset, user_data->distorted_file) != (size_t)user_data->offset) |
1883 | | { |
1884 | | x265_log(NULL, X265_LOG_ERROR, "distorted fread to skip u and v failed.\n"); |
1885 | | goto fail_or_end; |
1886 | | } |
1887 | | } |
1888 | | else |
1889 | | { |
1890 | | x265_log(NULL, X265_LOG_ERROR, "Invalid format\n"); |
1891 | | goto fail_or_end; |
1892 | | } |
1893 | | |
1894 | | |
1895 | | fail_or_end: |
1896 | | return ret; |
1897 | | } |
1898 | | |
1899 | | double x265_calculate_vmafscore(x265_param *param, x265_vmaf_data *data) |
1900 | | { |
1901 | | double score; |
1902 | | |
1903 | | data->width = param->sourceWidth; |
1904 | | data->height = param->sourceHeight; |
1905 | | data->internalBitDepth = param->internalBitDepth; |
1906 | | |
1907 | | if (param->internalCsp == X265_CSP_I420) |
1908 | | { |
1909 | | if ((param->sourceWidth * param->sourceHeight) % 2 != 0) |
1910 | | x265_log(NULL, X265_LOG_ERROR, "Invalid file size\n"); |
1911 | | data->offset = param->sourceWidth * param->sourceHeight / 2; |
1912 | | } |
1913 | | else if (param->internalCsp == X265_CSP_I422) |
1914 | | data->offset = param->sourceWidth * param->sourceHeight; |
1915 | | else if (param->internalCsp == X265_CSP_I444) |
1916 | | data->offset = param->sourceWidth * param->sourceHeight * 2; |
1917 | | else |
1918 | | x265_log(NULL, X265_LOG_ERROR, "Invalid format\n"); |
1919 | | |
1920 | | compute_vmaf(&score, vcd->format, data->width, data->height, read_frame, data, vcd->model_path, vcd->log_path, vcd->log_fmt, vcd->disable_clip, vcd->disable_avx, vcd->enable_transform, vcd->phone_model, vcd->psnr, vcd->ssim, vcd->ms_ssim, vcd->pool, vcd->thread, vcd->subsample, vcd->enable_conf_interval); |
1921 | | |
1922 | | return score; |
1923 | | } |
1924 | | |
1925 | | int read_frame_10bit(float *reference_data, float *distorted_data, float *temp_data, int stride, void *s) |
1926 | | { |
1927 | | x265_vmaf_framedata *user_data = (x265_vmaf_framedata *)s; |
1928 | | |
1929 | | PicYuv *reference_frame = (PicYuv *)user_data->reference_frame; |
1930 | | PicYuv *distorted_frame = (PicYuv *)user_data->distorted_frame; |
1931 | | |
1932 | | if(!user_data->frame_set) { |
1933 | | |
1934 | | int reference_stride = reference_frame->m_stride; |
1935 | | int distorted_stride = distorted_frame->m_stride; |
1936 | | |
1937 | | const uint16_t *reference_ptr = (const uint16_t *)reference_frame->m_picOrg[0]; |
1938 | | const uint16_t *distorted_ptr = (const uint16_t *)distorted_frame->m_picOrg[0]; |
1939 | | |
1940 | | temp_data = reference_data; |
1941 | | |
1942 | | int height = user_data->height; |
1943 | | int width = user_data->width; |
1944 | | |
1945 | | int i,j; |
1946 | | for (i = 0; i < height; i++) { |
1947 | | for ( j = 0; j < width; j++) { |
1948 | | temp_data[j] = ((float)reference_ptr[j] / 4.0); |
1949 | | } |
1950 | | reference_ptr += reference_stride; |
1951 | | temp_data += stride / sizeof(*temp_data); |
1952 | | } |
1953 | | |
1954 | | temp_data = distorted_data; |
1955 | | for (i = 0; i < height; i++) { |
1956 | | for (j = 0; j < width; j++) { |
1957 | | temp_data[j] = ((float)distorted_ptr[j] / 4.0); |
1958 | | } |
1959 | | distorted_ptr += distorted_stride; |
1960 | | temp_data += stride / sizeof(*temp_data); |
1961 | | } |
1962 | | |
1963 | | user_data->frame_set = 1; |
1964 | | return 0; |
1965 | | } |
1966 | | return 2; |
1967 | | } |
1968 | | |
1969 | | int read_frame_8bit(float *reference_data, float *distorted_data, float *temp_data, int stride, void *s) |
1970 | | { |
1971 | | x265_vmaf_framedata *user_data = (x265_vmaf_framedata *)s; |
1972 | | |
1973 | | PicYuv *reference_frame = (PicYuv *)user_data->reference_frame; |
1974 | | PicYuv *distorted_frame = (PicYuv *)user_data->distorted_frame; |
1975 | | |
1976 | | if(!user_data->frame_set) { |
1977 | | |
1978 | | int reference_stride = reference_frame->m_stride; |
1979 | | int distorted_stride = distorted_frame->m_stride; |
1980 | | |
1981 | | const uint8_t *reference_ptr = (const uint8_t *)reference_frame->m_picOrg[0]; |
1982 | | const uint8_t *distorted_ptr = (const uint8_t *)distorted_frame->m_picOrg[0]; |
1983 | | |
1984 | | temp_data = reference_data; |
1985 | | |
1986 | | int height = user_data->height; |
1987 | | int width = user_data->width; |
1988 | | |
1989 | | int i,j; |
1990 | | for (i = 0; i < height; i++) { |
1991 | | for ( j = 0; j < width; j++) { |
1992 | | temp_data[j] = (float)reference_ptr[j]; |
1993 | | } |
1994 | | reference_ptr += reference_stride; |
1995 | | temp_data += stride / sizeof(*temp_data); |
1996 | | } |
1997 | | |
1998 | | temp_data = distorted_data; |
1999 | | for (i = 0; i < height; i++) { |
2000 | | for (j = 0; j < width; j++) { |
2001 | | temp_data[j] = (float)distorted_ptr[j]; |
2002 | | } |
2003 | | distorted_ptr += distorted_stride; |
2004 | | temp_data += stride / sizeof(*temp_data); |
2005 | | } |
2006 | | |
2007 | | user_data->frame_set = 1; |
2008 | | return 0; |
2009 | | } |
2010 | | return 2; |
2011 | | } |
2012 | | |
2013 | | double x265_calculate_vmaf_framelevelscore(x265_vmaf_framedata *vmafframedata) |
2014 | | { |
2015 | | double score; |
2016 | | int (*read_frame)(float *reference_data, float *distorted_data, float *temp_data, |
2017 | | int stride, void *s); |
2018 | | if (vmafframedata->internalBitDepth == 8) |
2019 | | read_frame = read_frame_8bit; |
2020 | | else |
2021 | | read_frame = read_frame_10bit; |
2022 | | compute_vmaf(&score, vcd->format, vmafframedata->width, vmafframedata->height, read_frame, vmafframedata, vcd->model_path, vcd->log_path, vcd->log_fmt, vcd->disable_clip, vcd->disable_avx, vcd->enable_transform, vcd->phone_model, vcd->psnr, vcd->ssim, vcd->ms_ssim, vcd->pool, vcd->thread, vcd->subsample, vcd->enable_conf_interval); |
2023 | | |
2024 | | return score; |
2025 | | } |
2026 | | #endif |
2027 | | |
2028 | | } /* end namespace or extern "C" */ |
2029 | | |
2030 | | namespace X265_NS { |
2031 | | #ifdef SVT_HEVC |
2032 | | |
2033 | | void svt_initialise_app_context(x265_encoder *enc) |
2034 | | { |
2035 | | Encoder *encoder = static_cast<Encoder*>(enc); |
2036 | | |
2037 | | //Initialise Application Context |
2038 | | encoder->m_svtAppData = (SvtAppContext*)x265_malloc(sizeof(SvtAppContext)); |
2039 | | encoder->m_svtAppData->svtHevcParams = (EB_H265_ENC_CONFIGURATION*)x265_malloc(sizeof(EB_H265_ENC_CONFIGURATION)); |
2040 | | encoder->m_svtAppData->byteCount = 0; |
2041 | | encoder->m_svtAppData->outFrameCount = 0; |
2042 | | } |
2043 | | |
2044 | | int svt_initialise_input_buffer(x265_encoder *enc) |
2045 | | { |
2046 | | Encoder *encoder = static_cast<Encoder*>(enc); |
2047 | | |
2048 | | //Initialise Input Buffer |
2049 | | encoder->m_svtAppData->inputPictureBuffer = (EB_BUFFERHEADERTYPE*)x265_malloc(sizeof(EB_BUFFERHEADERTYPE)); |
2050 | | EB_BUFFERHEADERTYPE *inputPtr = encoder->m_svtAppData->inputPictureBuffer; |
2051 | | inputPtr->pBuffer = (unsigned char*)x265_malloc(sizeof(EB_H265_ENC_INPUT)); |
2052 | | |
2053 | | EB_H265_ENC_INPUT *inputData = (EB_H265_ENC_INPUT*)inputPtr->pBuffer; |
2054 | | inputData->dolbyVisionRpu.payload = NULL; |
2055 | | inputData->dolbyVisionRpu.payloadSize = 0; |
2056 | | |
2057 | | |
2058 | | if (!inputPtr->pBuffer) |
2059 | | return 0; |
2060 | | |
2061 | | inputPtr->nSize = sizeof(EB_BUFFERHEADERTYPE); |
2062 | | inputPtr->pAppPrivate = NULL; |
2063 | | return 1; |
2064 | | } |
2065 | | #endif // ifdef SVT_HEVC |
2066 | | |
2067 | | } // end namespace X265_NS |