# HG changeset patch # User Divya Manivannan <di...@multicorewareinc.com> # Date 1510141098 -19800 # Wed Nov 08 17:08:18 2017 +0530 # Node ID 563cbe1f4a21dcfe2117ccaa874b713d94434f92 # Parent bd438ce108435deb4f0063fca9a9e14a75e8de38 api: move csv and dither functions into api structure
csv is now moved into libx265. So, we can remove x265-extras.h diff -r bd438ce10843 -r 563cbe1f4a21 source/CMakeLists.txt --- a/source/CMakeLists.txt Wed Nov 08 16:18:29 2017 +0530 +++ b/source/CMakeLists.txt Wed Nov 08 17:08:18 2017 +0530 @@ -29,7 +29,7 @@ option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF) mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD) # X265_BUILD must be incremented each time the public API is changed -set(X265_BUILD 142) +set(X265_BUILD 143) configure_file("${PROJECT_SOURCE_DIR}/x265.def.in" "${PROJECT_BINARY_DIR}/x265.def") configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in" diff -r bd438ce10843 -r 563cbe1f4a21 source/encoder/CMakeLists.txt --- a/source/encoder/CMakeLists.txt Wed Nov 08 16:18:29 2017 +0530 +++ b/source/encoder/CMakeLists.txt Wed Nov 08 17:08:18 2017 +0530 @@ -43,5 +43,4 @@ reference.cpp reference.h encoder.cpp encoder.h api.cpp - weightPrediction.cpp - ../x265-extras.cpp ../x265-extras.h) + weightPrediction.cpp) diff -r bd438ce10843 -r 563cbe1f4a21 source/encoder/api.cpp --- a/source/encoder/api.cpp Wed Nov 08 16:18:29 2017 +0530 +++ b/source/encoder/api.cpp Wed Nov 08 17:08:18 2017 +0530 @@ -30,7 +30,6 @@ #include "level.h" #include "nal.h" #include "bitcost.h" -#include "x265-extras.h" /* multilib namespace reflectors */ #if LINKED_8BIT @@ -63,6 +62,14 @@ namespace X265_NS { #endif +static const char* summaryCSVHeader = + "Command, Date/Time, Elapsed Time, FPS, Bitrate, " + "Y PSNR, U PSNR, V PSNR, Global PSNR, SSIM, SSIM (dB), " + "I count, I ave-QP, I kbps, I-PSNR Y, I-PSNR U, I-PSNR V, I-SSIM (dB), " + "P count, P ave-QP, P kbps, P-PSNR Y, P-PSNR U, P-PSNR V, P-SSIM (dB), " + "B count, B ave-QP, B kbps, B-PSNR Y, B-PSNR U, B-PSNR V, B-SSIM (dB), " + "MaxCLL, MaxFALL, Version\n"; + x265_encoder *x265_encoder_open(x265_param *p) { if (!p) @@ -120,7 +127,7 @@ /* Try to open CSV file handle */ if (encoder->m_param->csvfn) { - encoder->m_param->csvfpt = x265_csvlog_open(*encoder->m_param, encoder->m_param->csvfn, encoder->m_param->csvLogLevel); + encoder->m_param->csvfpt = x265_csvlog_open(*encoder->m_param); if (!encoder->m_param->csvfpt) { x265_log(encoder->m_param, X265_LOG_ERROR, "Unable to open CSV log file <%s>, aborting\n", encoder->m_param->csvfn); @@ -280,7 +287,7 @@ *pi_nal = 0; if (numEncoded && encoder->m_param->csvLogLevel) - x265_csvlog_frame(encoder->m_param->csvfpt, *encoder->m_param, *pic_out, encoder->m_param->csvLogLevel); + x265_csvlog_frame(*encoder->m_param, *pic_out); if (numEncoded < 0) encoder->m_aborted = true; @@ -303,11 +310,8 @@ { Encoder *encoder = static_cast<Encoder*>(enc); x265_stats stats; - int padx = encoder->m_sps.conformanceWindow.rightOffset; - int pady = encoder->m_sps.conformanceWindow.bottomOffset; encoder->fetchStats(&stats, sizeof(stats)); - const x265_api * api = x265_api_get(0); - x265_csvlog_encode(encoder->m_param->csvfpt, api->version_str, *encoder->m_param, padx, pady, stats, encoder->m_param->csvLogLevel, argc, argv); + x265_csvlog_encode(enc, stats, argc, argv); } } @@ -436,6 +440,10 @@ &x265_encoder_ctu_info, &x265_get_slicetype_poc_and_scenecut, &x265_get_ref_frame_list, + &x265_csvlog_open, + &x265_csvlog_frame, + &x265_csvlog_encode, + &x265_dither_image, }; typedef const x265_api* (*api_get_func)(int bitDepth); @@ -630,4 +638,422 @@ return &libapi; } +FILE* x265_csvlog_open(const x265_param& param) +{ + FILE *csvfp = x265_fopen(param.csvfn, "r"); + if (csvfp) + { + /* file already exists, re-open for append */ + fclose(csvfp); + return x265_fopen(param.csvfn, "ab"); + } + else + { + /* new CSV file, write header */ + csvfp = x265_fopen(param.csvfn, "wb"); + if (csvfp) + { + if (param.csvLogLevel) + { + fprintf(csvfp, "Encode Order, Type, POC, QP, Bits, Scenecut, "); + if (param.csvLogLevel >= 2) + fprintf(csvfp, "I/P cost ratio, "); + if (param.rc.rateControlMode == X265_RC_CRF) + fprintf(csvfp, "RateFactor, "); + if (param.rc.vbvBufferSize) + fprintf(csvfp, "BufferFill, "); + if (param.bEnablePsnr) + fprintf(csvfp, "Y PSNR, U PSNR, V PSNR, YUV PSNR, "); + if (param.bEnableSsim) + fprintf(csvfp, "SSIM, SSIM(dB), "); + fprintf(csvfp, "Latency, "); + fprintf(csvfp, "List 0, List 1"); + uint32_t size = param.maxCUSize; + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Intra %dx%d DC, Intra %dx%d Planar, Intra %dx%d Ang", size, size, size, size, size, size); + size /= 2; + } + fprintf(csvfp, ", 4x4"); + size = param.maxCUSize; + if (param.bEnableRectInter) + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Inter %dx%d, Inter %dx%d (Rect)", size, size, size, size); + if (param.bEnableAMP) + fprintf(csvfp, ", Inter %dx%d (Amp)", size, size); + size /= 2; + } + } + else + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Inter %dx%d", size, size); + size /= 2; + } + } + size = param.maxCUSize; + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Skip %dx%d", size, size); + size /= 2; + } + size = param.maxCUSize; + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Merge %dx%d", size, size); + size /= 2; + } + + if (param.csvLogLevel >= 2) + { + fprintf(csvfp, ", Avg Luma Distortion, Avg Chroma Distortion, Avg psyEnergy, Avg Residual Energy," + " Min Luma Level, Max Luma Level, Avg Luma Level"); + + if (param.internalCsp != X265_CSP_I400) + fprintf(csvfp, ", Min Cb Level, Max Cb Level, Avg Cb Level, Min Cr Level, Max Cr Level, Avg Cr Level"); + + /* PU statistics */ + size = param.maxCUSize; + for (uint32_t i = 0; i< param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) + { + fprintf(csvfp, ", Intra %dx%d", size, size); + fprintf(csvfp, ", Skip %dx%d", size, size); + fprintf(csvfp, ", AMP %d", size); + fprintf(csvfp, ", Inter %dx%d", size, size); + fprintf(csvfp, ", Merge %dx%d", size, size); + fprintf(csvfp, ", Inter %dx%d", size, size / 2); + fprintf(csvfp, ", Merge %dx%d", size, size / 2); + fprintf(csvfp, ", Inter %dx%d", size / 2, size); + fprintf(csvfp, ", Merge %dx%d", size / 2, size); + size /= 2; + } + + if ((uint32_t)g_log2Size[param.minCUSize] == 3) + fprintf(csvfp, ", 4x4"); + + /* detailed performance statistics */ + fprintf(csvfp, ", DecideWait (ms), Row0Wait (ms), Wall time (ms), Ref Wait Wall (ms), Total CTU time (ms)," + "Stall Time (ms), Total frame time (ms), Avg WPP, Row Blocks"); + } + fprintf(csvfp, "\n"); + } + else + fputs(summaryCSVHeader, csvfp); + } + return csvfp; + } +} + +// per frame CSV logging +void x265_csvlog_frame(const x265_param& param, const x265_picture& pic) +{ + if (!param.csvfpt) + return; + + const x265_frame_stats* frameStats = &pic.frameData; + fprintf(param.csvfpt, "%d, %c-SLICE, %4d, %2.2lf, %10d, %d,", frameStats->encoderOrder, frameStats->sliceType, frameStats->poc, + frameStats->qp, (int)frameStats->bits, frameStats->bScenecut); + if (param.csvLogLevel >= 2) + fprintf(param.csvfpt, "%.2f,", frameStats->ipCostRatio); + if (param.rc.rateControlMode == X265_RC_CRF) + fprintf(param.csvfpt, "%.3lf,", frameStats->rateFactor); + if (param.rc.vbvBufferSize) + fprintf(param.csvfpt, "%.3lf,", frameStats->bufferFill); + if (param.bEnablePsnr) + fprintf(param.csvfpt, "%.3lf, %.3lf, %.3lf, %.3lf,", frameStats->psnrY, frameStats->psnrU, frameStats->psnrV, frameStats->psnr); + if (param.bEnableSsim) + fprintf(param.csvfpt, " %.6f, %6.3f,", frameStats->ssim, x265_ssim2dB(frameStats->ssim)); + fprintf(param.csvfpt, "%d, ", frameStats->frameLatency); + if (frameStats->sliceType == 'I' || frameStats->sliceType == 'i') + fputs(" -, -,", param.csvfpt); + else + { + int i = 0; + while (frameStats->list0POC[i] != -1) + fprintf(param.csvfpt, "%d ", frameStats->list0POC[i++]); + fprintf(param.csvfpt, ","); + if (frameStats->sliceType != 'P') + { + i = 0; + while (frameStats->list1POC[i] != -1) + fprintf(param.csvfpt, "%d ", frameStats->list1POC[i++]); + fprintf(param.csvfpt, ","); + } + else + fputs(" -,", param.csvfpt); + } + + if (param.csvLogLevel) + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, "%5.2lf%%, %5.2lf%%, %5.2lf%%,", frameStats->cuStats.percentIntraDistribution[depth][0], + frameStats->cuStats.percentIntraDistribution[depth][1], + frameStats->cuStats.percentIntraDistribution[depth][2]); + fprintf(param.csvfpt, "%5.2lf%%", frameStats->cuStats.percentIntraNxN); + if (param.bEnableRectInter) + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(param.csvfpt, ", %5.2lf%%, %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0], + frameStats->cuStats.percentInterDistribution[depth][1]); + if (param.bEnableAMP) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][2]); + } + } + else + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0]); + } + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentSkipCu[depth]); + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentMergeCu[depth]); + } + + if (param.csvLogLevel >= 2) + { + fprintf(param.csvfpt, ", %.2lf, %.2lf, %.2lf, %.2lf ", frameStats->avgLumaDistortion, + frameStats->avgChromaDistortion, + frameStats->avgPsyEnergy, + frameStats->avgResEnergy); + + fprintf(param.csvfpt, ", %d, %d, %.2lf", frameStats->minLumaLevel, frameStats->maxLumaLevel, frameStats->avgLumaLevel); + + if (param.internalCsp != X265_CSP_I400) + { + fprintf(param.csvfpt, ", %d, %d, %.2lf", frameStats->minChromaULevel, frameStats->maxChromaULevel, frameStats->avgChromaULevel); + fprintf(param.csvfpt, ", %d, %d, %.2lf", frameStats->minChromaVLevel, frameStats->maxChromaVLevel, frameStats->avgChromaVLevel); + } + + for (uint32_t i = 0; i < param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) + { + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentIntraPu[i]); + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentSkipPu[i]); + fprintf(param.csvfpt, ",%.2lf%%", frameStats->puStats.percentAmpPu[i]); + for (uint32_t j = 0; j < 3; j++) + { + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentInterPu[i][j]); + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentMergePu[i][j]); + } + } + if ((uint32_t)g_log2Size[param.minCUSize] == 3) + fprintf(param.csvfpt, ",%.2lf%%", frameStats->puStats.percentNxN); + + fprintf(param.csvfpt, ", %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf,", frameStats->decideWaitTime, frameStats->row0WaitTime, + frameStats->wallTime, frameStats->refWaitWallTime, + frameStats->totalCTUTime, frameStats->stallTime, + frameStats->totalFrameTime); + + fprintf(param.csvfpt, " %.3lf, %d", frameStats->avgWPP, frameStats->countRowBlocks); + } + fprintf(param.csvfpt, "\n"); + fflush(stderr); +} + +void x265_csvlog_encode(x265_encoder *enc, const x265_stats& stats, int argc, char** argv) +{ + if (enc) + { + Encoder *encoder = static_cast<Encoder*>(enc); + int padx = encoder->m_sps.conformanceWindow.rightOffset; + int pady = encoder->m_sps.conformanceWindow.bottomOffset; + const x265_api * api = x265_api_get(0); + + if (!encoder->m_param->csvfpt) + return; + + if (encoder->m_param->csvLogLevel) + { + // adding summary to a per-frame csv log file, so it needs a summary header + fprintf(encoder->m_param->csvfpt, "\nSummary\n"); + fputs(summaryCSVHeader, encoder->m_param->csvfpt); + } + + // CLI arguments or other + if (argc) + { + fputc('"', encoder->m_param->csvfpt); + for (int i = 1; i < argc; i++) + { + fputc(' ', encoder->m_param->csvfpt); + fputs(argv[i], encoder->m_param->csvfpt); + } + fputc('"', encoder->m_param->csvfpt); + } + else + { + const x265_param* paramTemp = encoder->m_param; + char *opts = x265_param2string((x265_param*)paramTemp, padx, pady); + if (opts) + { + fputc('"', encoder->m_param->csvfpt); + fputs(opts, encoder->m_param->csvfpt); + fputc('"', encoder->m_param->csvfpt); + } + } + + // current date and time + time_t now; + struct tm* timeinfo; + time(&now); + timeinfo = localtime(&now); + char buffer[200]; + strftime(buffer, 128, "%c", timeinfo); + fprintf(encoder->m_param->csvfpt, ", %s, ", buffer); + + // elapsed time, fps, bitrate + fprintf(encoder->m_param->csvfpt, "%.2f, %.2f, %.2f,", + stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate); + + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf, %.3lf,", + stats.globalPsnrY / stats.encodedPictureCount, stats.globalPsnrU / stats.encodedPictureCount, + stats.globalPsnrV / stats.encodedPictureCount, stats.globalPsnr); + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.6f, %6.3f,", stats.globalSsim, x265_ssim2dB(stats.globalSsim)); + else + fprintf(encoder->m_param->csvfpt, " -, -,"); + + if (stats.statsI.numPics) + { + fprintf(encoder->m_param->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats.statsI.numPics, stats.statsI.avgQp, stats.statsI.bitrate); + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf,", stats.statsI.psnrY, stats.statsI.psnrU, stats.statsI.psnrV); + else + fprintf(encoder->m_param->csvfpt, " -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.3lf,", stats.statsI.ssim); + else + fprintf(encoder->m_param->csvfpt, " -,"); + } + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -, -, -, -,"); + + if (stats.statsP.numPics) + { + fprintf(encoder->m_param->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats.statsP.numPics, stats.statsP.avgQp, stats.statsP.bitrate); + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf,", stats.statsP.psnrY, stats.statsP.psnrU, stats.statsP.psnrV); + else + fprintf(encoder->m_param->csvfpt, " -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.3lf,", stats.statsP.ssim); + else + fprintf(encoder->m_param->csvfpt, " -,"); + } + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -, -, -, -,"); + + if (stats.statsB.numPics) + { + fprintf(encoder->m_param->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats.statsB.numPics, stats.statsB.avgQp, stats.statsB.bitrate); + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf,", stats.statsB.psnrY, stats.statsB.psnrU, stats.statsB.psnrV); + else + fprintf(encoder->m_param->csvfpt, " -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.3lf,", stats.statsB.ssim); + else + fprintf(encoder->m_param->csvfpt, " -,"); + } + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -, -, -, -,"); + + fprintf(encoder->m_param->csvfpt, " %-6u, %-6u, %s\n", stats.maxCLL, stats.maxFALL, api->version_str); + } +} + +/* The dithering algorithm is based on Sierra-2-4A error diffusion. + * We convert planes in place (without allocating a new buffer). */ +static void ditherPlane(uint16_t *src, int srcStride, int width, int height, int16_t *errors, int bitDepth) +{ + const int lShift = 16 - bitDepth; + const int rShift = 16 - bitDepth + 2; + const int half = (1 << (16 - bitDepth + 1)); + const int pixelMax = (1 << bitDepth) - 1; + + memset(errors, 0, (width + 1) * sizeof(int16_t)); + + if (bitDepth == 8) + { + for (int y = 0; y < height; y++, src += srcStride) + { + uint8_t* dst = (uint8_t *)src; + int16_t err = 0; + for (int x = 0; x < width; x++) + { + err = err * 2 + errors[x] + errors[x + 1]; + int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); + errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); + dst[x] = (uint8_t)tmpDst; + } + } + } + else + { + for (int y = 0; y < height; y++, src += srcStride) + { + int16_t err = 0; + for (int x = 0; x < width; x++) + { + err = err * 2 + errors[x] + errors[x + 1]; + int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); + errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); + src[x] = (uint16_t)tmpDst; + } + } + } +} + +void x265_dither_image(x265_picture& picIn, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth) +{ + const x265_api* api = x265_api_get(0); + + if (sizeof(x265_picture) != api->sizeof_picture) + { + fprintf(stderr, "extras [error]: structure size skew, unable to dither\n"); + return; + } + + if (picIn.bitDepth <= 8) + { + fprintf(stderr, "extras [error]: dither support enabled only for input bitdepth > 8\n"); + return; + } + + if (picIn.bitDepth == bitDepth) + { + fprintf(stderr, "extras[error]: dither support enabled only if encoder depth is different from picture depth\n"); + return; + } + + /* This portion of code is from readFrame in x264. */ + for (int i = 0; i < x265_cli_csps[picIn.colorSpace].planes; i++) + { + if (picIn.bitDepth < 16) + { + /* upconvert non 16bit high depth planes to 16bit */ + uint16_t *plane = (uint16_t*)picIn.planes[i]; + uint32_t pixelCount = x265_picturePlaneSize(picIn.colorSpace, picWidth, picHeight, i); + int lShift = 16 - picIn.bitDepth; + + /* This loop assumes width is equal to stride which + * happens to be true for file reader outputs */ + for (uint32_t j = 0; j < pixelCount; j++) + plane[j] = plane[j] << lShift; + } + + int height = (int)(picHeight >> x265_cli_csps[picIn.colorSpace].height[i]); + int width = (int)(picWidth >> x265_cli_csps[picIn.colorSpace].width[i]); + + ditherPlane(((uint16_t*)picIn.planes[i]), picIn.stride[i] / 2, width, height, errorBuf, bitDepth); + } +} + } /* end namespace or extern "C" */ diff -r bd438ce10843 -r 563cbe1f4a21 source/x265-extras.cpp --- a/source/x265-extras.cpp Wed Nov 08 16:18:29 2017 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,447 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2013-2017 MulticoreWare, Inc - * - * Authors: Steve Borho <st...@borho.org> - * Selvakumar Nithiyaruban <selvaku...@multicorewareinc.com> - * Divya Manivannan <di...@multicorewareinc.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. - * - * This program is also available under a commercial proprietary license. - * For more information, contact us at license @ x265.com. - *****************************************************************************/ - -#include "x265.h" -#include "x265-extras.h" -#include "param.h" -#include "common.h" - -using namespace X265_NS; - -static const char* summaryCSVHeader = - "Command, Date/Time, Elapsed Time, FPS, Bitrate, " - "Y PSNR, U PSNR, V PSNR, Global PSNR, SSIM, SSIM (dB), " - "I count, I ave-QP, I kbps, I-PSNR Y, I-PSNR U, I-PSNR V, I-SSIM (dB), " - "P count, P ave-QP, P kbps, P-PSNR Y, P-PSNR U, P-PSNR V, P-SSIM (dB), " - "B count, B ave-QP, B kbps, B-PSNR Y, B-PSNR U, B-PSNR V, B-SSIM (dB), " - "MaxCLL, MaxFALL, Version\n"; - -FILE* x265_csvlog_open(const x265_param& param, const char* fname, int level) -{ - FILE *csvfp = x265_fopen(fname, "r"); - if (csvfp) - { - /* file already exists, re-open for append */ - fclose(csvfp); - return x265_fopen(fname, "ab"); - } - else - { - /* new CSV file, write header */ - csvfp = x265_fopen(fname, "wb"); - if (csvfp) - { - if (level) - { - fprintf(csvfp, "Encode Order, Type, POC, QP, Bits, Scenecut, "); - if (level >= 2) - fprintf(csvfp, "I/P cost ratio, "); - if (param.rc.rateControlMode == X265_RC_CRF) - fprintf(csvfp, "RateFactor, "); - if (param.rc.vbvBufferSize) - fprintf(csvfp, "BufferFill, "); - if (param.bEnablePsnr) - fprintf(csvfp, "Y PSNR, U PSNR, V PSNR, YUV PSNR, "); - if (param.bEnableSsim) - fprintf(csvfp, "SSIM, SSIM(dB), "); - fprintf(csvfp, "Latency, "); - fprintf(csvfp, "List 0, List 1"); - uint32_t size = param.maxCUSize; - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Intra %dx%d DC, Intra %dx%d Planar, Intra %dx%d Ang", size, size, size, size, size, size); - size /= 2; - } - fprintf(csvfp, ", 4x4"); - size = param.maxCUSize; - if (param.bEnableRectInter) - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Inter %dx%d, Inter %dx%d (Rect)", size, size, size, size); - if (param.bEnableAMP) - fprintf(csvfp, ", Inter %dx%d (Amp)", size, size); - size /= 2; - } - } - else - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Inter %dx%d", size, size); - size /= 2; - } - } - size = param.maxCUSize; - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Skip %dx%d", size, size); - size /= 2; - } - size = param.maxCUSize; - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Merge %dx%d", size, size); - size /= 2; - } - - if (level >= 2) - { - fprintf(csvfp, ", Avg Luma Distortion, Avg Chroma Distortion, Avg psyEnergy, Avg Residual Energy," - " Min Luma Level, Max Luma Level, Avg Luma Level"); - - if (param.internalCsp != X265_CSP_I400) - fprintf(csvfp, ", Min Cb Level, Max Cb Level, Avg Cb Level, Min Cr Level, Max Cr Level, Avg Cr Level"); - - /* PU statistics */ - size = param.maxCUSize; - for (uint32_t i = 0; i< param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) - { - fprintf(csvfp, ", Intra %dx%d", size, size); - fprintf(csvfp, ", Skip %dx%d", size, size); - fprintf(csvfp, ", AMP %d", size); - fprintf(csvfp, ", Inter %dx%d", size, size); - fprintf(csvfp, ", Merge %dx%d", size, size); - fprintf(csvfp, ", Inter %dx%d", size, size / 2); - fprintf(csvfp, ", Merge %dx%d", size, size / 2); - fprintf(csvfp, ", Inter %dx%d", size / 2, size); - fprintf(csvfp, ", Merge %dx%d", size / 2, size); - size /= 2; - } - - if ((uint32_t)g_log2Size[param.minCUSize] == 3) - fprintf(csvfp, ", 4x4"); - - /* detailed performance statistics */ - fprintf(csvfp, ", DecideWait (ms), Row0Wait (ms), Wall time (ms), Ref Wait Wall (ms), Total CTU time (ms)," - "Stall Time (ms), Total frame time (ms), Avg WPP, Row Blocks"); - } - fprintf(csvfp, "\n"); - } - else - fputs(summaryCSVHeader, csvfp); - } - return csvfp; - } -} - -// per frame CSV logging -void x265_csvlog_frame(FILE* csvfp, const x265_param& param, const x265_picture& pic, int level) -{ - if (!csvfp) - return; - - const x265_frame_stats* frameStats = &pic.frameData; - fprintf(csvfp, "%d, %c-SLICE, %4d, %2.2lf, %10d, %d,", frameStats->encoderOrder, frameStats->sliceType, frameStats->poc, - frameStats->qp, (int)frameStats->bits, frameStats->bScenecut); - if (level >= 2) - fprintf(csvfp, "%.2f,", frameStats->ipCostRatio); - if (param.rc.rateControlMode == X265_RC_CRF) - fprintf(csvfp, "%.3lf,", frameStats->rateFactor); - if (param.rc.vbvBufferSize) - fprintf(csvfp, "%.3lf,", frameStats->bufferFill); - if (param.bEnablePsnr) - fprintf(csvfp, "%.3lf, %.3lf, %.3lf, %.3lf,", frameStats->psnrY, frameStats->psnrU, frameStats->psnrV, frameStats->psnr); - if (param.bEnableSsim) - fprintf(csvfp, " %.6f, %6.3f,", frameStats->ssim, x265_ssim2dB(frameStats->ssim)); - fprintf(csvfp, "%d, ", frameStats->frameLatency); - if (frameStats->sliceType == 'I' || frameStats->sliceType == 'i') - fputs(" -, -,", csvfp); - else - { - int i = 0; - while (frameStats->list0POC[i] != -1) - fprintf(csvfp, "%d ", frameStats->list0POC[i++]); - fprintf(csvfp, ","); - if (frameStats->sliceType != 'P') - { - i = 0; - while (frameStats->list1POC[i] != -1) - fprintf(csvfp, "%d ", frameStats->list1POC[i++]); - fprintf(csvfp, ","); - } - else - fputs(" -,", csvfp); - } - - if (level) - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, "%5.2lf%%, %5.2lf%%, %5.2lf%%,", frameStats->cuStats.percentIntraDistribution[depth][0], - frameStats->cuStats.percentIntraDistribution[depth][1], - frameStats->cuStats.percentIntraDistribution[depth][2]); - fprintf(csvfp, "%5.2lf%%", frameStats->cuStats.percentIntraNxN); - if (param.bEnableRectInter) - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", %5.2lf%%, %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0], - frameStats->cuStats.percentInterDistribution[depth][1]); - if (param.bEnableAMP) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][2]); - } - } - else - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0]); - } - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentSkipCu[depth]); - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentMergeCu[depth]); - } - - if (level >= 2) - { - fprintf(csvfp, ", %.2lf, %.2lf, %.2lf, %.2lf ", frameStats->avgLumaDistortion, - frameStats->avgChromaDistortion, - frameStats->avgPsyEnergy, - frameStats->avgResEnergy); - - fprintf(csvfp, ", %d, %d, %.2lf", frameStats->minLumaLevel, frameStats->maxLumaLevel, frameStats->avgLumaLevel); - - if (param.internalCsp != X265_CSP_I400) - { - fprintf(csvfp, ", %d, %d, %.2lf", frameStats->minChromaULevel, frameStats->maxChromaULevel, frameStats->avgChromaULevel); - fprintf(csvfp, ", %d, %d, %.2lf", frameStats->minChromaVLevel, frameStats->maxChromaVLevel, frameStats->avgChromaVLevel); - } - - for (uint32_t i = 0; i < param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) - { - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentIntraPu[i]); - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentSkipPu[i]); - fprintf(csvfp, ",%.2lf%%", frameStats->puStats.percentAmpPu[i]); - for (uint32_t j = 0; j < 3; j++) - { - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentInterPu[i][j]); - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentMergePu[i][j]); - } - } - if ((uint32_t)g_log2Size[param.minCUSize] == 3) - fprintf(csvfp, ",%.2lf%%", frameStats->puStats.percentNxN); - - fprintf(csvfp, ", %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf,", frameStats->decideWaitTime, frameStats->row0WaitTime, - frameStats->wallTime, frameStats->refWaitWallTime, - frameStats->totalCTUTime, frameStats->stallTime, - frameStats->totalFrameTime); - - fprintf(csvfp, " %.3lf, %d", frameStats->avgWPP, frameStats->countRowBlocks); - } - fprintf(csvfp, "\n"); - fflush(stderr); -} - -void x265_csvlog_encode(FILE* csvfp, const char* version, const x265_param& param, int padx, int pady, const x265_stats& stats, int level, int argc, char** argv) -{ - if (!csvfp) - return; - - if (level) - { - // adding summary to a per-frame csv log file, so it needs a summary header - fprintf(csvfp, "\nSummary\n"); - fputs(summaryCSVHeader, csvfp); - } - - // CLI arguments or other - if (argc) - { - fputc('"', csvfp); - for (int i = 1; i < argc; i++) - { - fputc(' ', csvfp); - fputs(argv[i], csvfp); - } - fputc('"', csvfp); - } - else - { - const x265_param* paramTemp = ¶m; - char *opts = x265_param2string((x265_param*)paramTemp, padx, pady); - if (opts) - { - fputc('"', csvfp); - fputs(opts, csvfp); - fputc('"', csvfp); - } - } - - // current date and time - time_t now; - struct tm* timeinfo; - time(&now); - timeinfo = localtime(&now); - char buffer[200]; - strftime(buffer, 128, "%c", timeinfo); - fprintf(csvfp, ", %s, ", buffer); - - // elapsed time, fps, bitrate - fprintf(csvfp, "%.2f, %.2f, %.2f,", - stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate); - - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf, %.3lf,", - stats.globalPsnrY / stats.encodedPictureCount, stats.globalPsnrU / stats.encodedPictureCount, - stats.globalPsnrV / stats.encodedPictureCount, stats.globalPsnr); - else - fprintf(csvfp, " -, -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.6f, %6.3f,", stats.globalSsim, x265_ssim2dB(stats.globalSsim)); - else - fprintf(csvfp, " -, -,"); - - if (stats.statsI.numPics) - { - fprintf(csvfp, " %-6u, %2.2lf, %-8.2lf,", stats.statsI.numPics, stats.statsI.avgQp, stats.statsI.bitrate); - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf,", stats.statsI.psnrY, stats.statsI.psnrU, stats.statsI.psnrV); - else - fprintf(csvfp, " -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.3lf,", stats.statsI.ssim); - else - fprintf(csvfp, " -,"); - } - else - fprintf(csvfp, " -, -, -, -, -, -, -,"); - - if (stats.statsP.numPics) - { - fprintf(csvfp, " %-6u, %2.2lf, %-8.2lf,", stats.statsP.numPics, stats.statsP.avgQp, stats.statsP.bitrate); - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf,", stats.statsP.psnrY, stats.statsP.psnrU, stats.statsP.psnrV); - else - fprintf(csvfp, " -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.3lf,", stats.statsP.ssim); - else - fprintf(csvfp, " -,"); - } - else - fprintf(csvfp, " -, -, -, -, -, -, -,"); - - if (stats.statsB.numPics) - { - fprintf(csvfp, " %-6u, %2.2lf, %-8.2lf,", stats.statsB.numPics, stats.statsB.avgQp, stats.statsB.bitrate); - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf,", stats.statsB.psnrY, stats.statsB.psnrU, stats.statsB.psnrV); - else - fprintf(csvfp, " -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.3lf,", stats.statsB.ssim); - else - fprintf(csvfp, " -,"); - } - else - fprintf(csvfp, " -, -, -, -, -, -, -,"); - - fprintf(csvfp, " %-6u, %-6u, %s\n", stats.maxCLL, stats.maxFALL, version); -} - -/* The dithering algorithm is based on Sierra-2-4A error diffusion. - * We convert planes in place (without allocating a new buffer). */ -static void ditherPlane(uint16_t *src, int srcStride, int width, int height, int16_t *errors, int bitDepth) -{ - const int lShift = 16 - bitDepth; - const int rShift = 16 - bitDepth + 2; - const int half = (1 << (16 - bitDepth + 1)); - const int pixelMax = (1 << bitDepth) - 1; - - memset(errors, 0, (width + 1) * sizeof(int16_t)); - - if (bitDepth == 8) - { - for (int y = 0; y < height; y++, src += srcStride) - { - uint8_t* dst = (uint8_t *)src; - int16_t err = 0; - for (int x = 0; x < width; x++) - { - err = err * 2 + errors[x] + errors[x + 1]; - int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); - errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); - dst[x] = (uint8_t)tmpDst; - } - } - } - else - { - for (int y = 0; y < height; y++, src += srcStride) - { - int16_t err = 0; - for (int x = 0; x < width; x++) - { - err = err * 2 + errors[x] + errors[x + 1]; - int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); - errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); - src[x] = (uint16_t)tmpDst; - } - } - } -} - -void x265_dither_image(const x265_api& api, x265_picture& picIn, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth) -{ - if (sizeof(x265_picture) != api.sizeof_picture) - { - fprintf(stderr, "extras [error]: structure size skew, unable to dither\n"); - return; - } - - if (picIn.bitDepth <= 8) - { - fprintf(stderr, "extras [error]: dither support enabled only for input bitdepth > 8\n"); - return; - } - - if (picIn.bitDepth == bitDepth) - { - fprintf(stderr, "extras[error]: dither support enabled only if encoder depth is different from picture depth\n"); - return; - } - - /* This portion of code is from readFrame in x264. */ - for (int i = 0; i < x265_cli_csps[picIn.colorSpace].planes; i++) - { - if (picIn.bitDepth < 16) - { - /* upconvert non 16bit high depth planes to 16bit */ - uint16_t *plane = (uint16_t*)picIn.planes[i]; - uint32_t pixelCount = x265_picturePlaneSize(picIn.colorSpace, picWidth, picHeight, i); - int lShift = 16 - picIn.bitDepth; - - /* This loop assumes width is equal to stride which - * happens to be true for file reader outputs */ - for (uint32_t j = 0; j < pixelCount; j++) - plane[j] = plane[j] << lShift; - } - - int height = (int)(picHeight >> x265_cli_csps[picIn.colorSpace].height[i]); - int width = (int)(picWidth >> x265_cli_csps[picIn.colorSpace].width[i]); - - ditherPlane(((uint16_t*)picIn.planes[i]), picIn.stride[i] / 2, width, height, errorBuf, bitDepth); - } -} diff -r bd438ce10843 -r 563cbe1f4a21 source/x265-extras.h --- a/source/x265-extras.h Wed Nov 08 16:18:29 2017 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2013-2017 MulticoreWare, Inc - * - * Authors: Steve Borho <st...@borho.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. - * - * This program is also available under a commercial proprietary license. - * For more information, contact us at license @ x265.com. - *****************************************************************************/ - -#ifndef X265_EXTRAS_H -#define X265_EXTRAS_H 1 - -#include "x265.h" - -#include <stdio.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#if _WIN32 -#define LIBAPI __declspec(dllexport) -#else -#define LIBAPI -#endif - -/* Open a CSV log file. On success it returns a file handle which must be passed - * to x265_csvlog_frame() and/or x265_csvlog_encode(). The file handle must be - * closed by the caller using fclose(). If level is 0, then no frame logging - * header is written to the file. This function will return NULL if it is unable - * to open the file for write or if it detects a structure size skew */ -LIBAPI FILE* x265_csvlog_open(const x265_param& param, const char* fname, int level); - -/* Log frame statistics to the CSV file handle. level should have been non-zero - * in the call to x265_csvlog_open() if this function is called. */ -LIBAPI void x265_csvlog_frame(FILE* csvfp, const x265_param& param, const x265_picture& pic, int level); - -/* Log final encode statistics to the CSV file handle. 'argc' and 'argv' are - * intended to be command line arguments passed to the encoder. Encode - * statistics should be queried from the encoder just prior to closing it. */ -LIBAPI void x265_csvlog_encode(FILE* csvfp, const char* version, const x265_param& param, int padx, int pady, const x265_stats& stats, int level, int argc, char** argv); - -/* In-place downshift from a bit-depth greater than 8 to a bit-depth of 8, using - * the residual bits to dither each row. */ -LIBAPI void x265_dither_image(const x265_api& api, x265_picture&, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth); - -#ifdef __cplusplus -} -#endif - -#endif diff -r bd438ce10843 -r 563cbe1f4a21 source/x265.cpp --- a/source/x265.cpp Wed Nov 08 16:18:29 2017 +0530 +++ b/source/x265.cpp Wed Nov 08 17:08:18 2017 +0530 @@ -26,7 +26,6 @@ #endif #include "x265.h" -#include "x265-extras.h" #include "x265cli.h" #include "input/input.h" @@ -639,7 +638,7 @@ { if (pic_in->bitDepth > param->internalBitDepth && cliopt.bDither) { - x265_dither_image(*api, *pic_in, cliopt.input->getWidth(), cliopt.input->getHeight(), errorBuf, param->internalBitDepth); + x265_dither_image(*pic_in, cliopt.input->getWidth(), cliopt.input->getHeight(), errorBuf, param->internalBitDepth); pic_in->bitDepth = param->internalBitDepth; } /* Overwrite PTS */ diff -r bd438ce10843 -r 563cbe1f4a21 source/x265.def.in --- a/source/x265.def.in Wed Nov 08 16:18:29 2017 +0530 +++ b/source/x265.def.in Wed Nov 08 17:08:18 2017 +0530 @@ -26,3 +26,7 @@ x265_encoder_ctu_info x265_get_slicetype_poc_and_scenecut x265_get_ref_frame_list +x265_csvlog_open +x265_csvlog_frame +x265_csvlog_encode +x265_dither_image diff -r bd438ce10843 -r 563cbe1f4a21 source/x265.h --- a/source/x265.h Wed Nov 08 16:18:29 2017 +0530 +++ b/source/x265.h Wed Nov 08 17:08:18 2017 +0530 @@ -1745,6 +1745,26 @@ void x265_cleanup(void); +/* Open a CSV log file. On success it returns a file handle which must be passed + * to x265_csvlog_frame() and/or x265_csvlog_encode(). The file handle must be + * closed by the caller using fclose(). If csv-loglevel is 0, then no frame logging + * header is written to the file. This function will return NULL if it is unable + * to open the file for write or if it detects a structure size skew */ +FILE* x265_csvlog_open(const x265_param& param); + +/* Log frame statistics to the CSV file handle. csv-loglevel should have been non-zero + * in the call to x265_csvlog_open() if this function is called. */ +void x265_csvlog_frame(const x265_param& param, const x265_picture& pic); + +/* Log final encode statistics to the CSV file handle. 'argc' and 'argv' are + * intended to be command line arguments passed to the encoder. Encode + * statistics should be queried from the encoder just prior to closing it. */ +void x265_csvlog_encode(x265_encoder *encoder, const x265_stats& stats, int argc, char** argv); + +/* In-place downshift from a bit-depth greater than 8 to a bit-depth of 8, using + * the residual bits to dither each row. */ +void x265_dither_image(x265_picture& pic, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth); + #define X265_MAJOR_VERSION 1 /* === Multi-lib API === @@ -1792,6 +1812,10 @@ int (*encoder_ctu_info)(x265_encoder*, int, x265_ctu_info_t**); int (*get_slicetype_poc_and_scenecut)(x265_encoder*, int*, int*, int*); int (*get_ref_frame_list)(x265_encoder*, x265_picyuv**, x265_picyuv**, int, int); + FILE* (*csvlog_open)(const x265_param&); + void (*csvlog_frame)(const x265_param&, const x265_picture&); + void (*csvlog_encode)(x265_encoder*, const x265_stats&, int, char**); + void (*dither_image)(x265_picture&, int, int, int16_t*, int); /* add new pointers to the end, or increment X265_MAJOR_VERSION */ } x265_api;
# HG changeset patch # User Divya Manivannan <di...@multicorewareinc.com> # Date 1510141098 -19800 # Wed Nov 08 17:08:18 2017 +0530 # Node ID 563cbe1f4a21dcfe2117ccaa874b713d94434f92 # Parent bd438ce108435deb4f0063fca9a9e14a75e8de38 api: move csv and dither functions into api structure csv is now moved into libx265. So, we can remove x265-extras.h diff -r bd438ce10843 -r 563cbe1f4a21 source/CMakeLists.txt --- a/source/CMakeLists.txt Wed Nov 08 16:18:29 2017 +0530 +++ b/source/CMakeLists.txt Wed Nov 08 17:08:18 2017 +0530 @@ -29,7 +29,7 @@ option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF) mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD) # X265_BUILD must be incremented each time the public API is changed -set(X265_BUILD 142) +set(X265_BUILD 143) configure_file("${PROJECT_SOURCE_DIR}/x265.def.in" "${PROJECT_BINARY_DIR}/x265.def") configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in" diff -r bd438ce10843 -r 563cbe1f4a21 source/encoder/CMakeLists.txt --- a/source/encoder/CMakeLists.txt Wed Nov 08 16:18:29 2017 +0530 +++ b/source/encoder/CMakeLists.txt Wed Nov 08 17:08:18 2017 +0530 @@ -43,5 +43,4 @@ reference.cpp reference.h encoder.cpp encoder.h api.cpp - weightPrediction.cpp - ../x265-extras.cpp ../x265-extras.h) + weightPrediction.cpp) diff -r bd438ce10843 -r 563cbe1f4a21 source/encoder/api.cpp --- a/source/encoder/api.cpp Wed Nov 08 16:18:29 2017 +0530 +++ b/source/encoder/api.cpp Wed Nov 08 17:08:18 2017 +0530 @@ -30,7 +30,6 @@ #include "level.h" #include "nal.h" #include "bitcost.h" -#include "x265-extras.h" /* multilib namespace reflectors */ #if LINKED_8BIT @@ -63,6 +62,14 @@ namespace X265_NS { #endif +static const char* summaryCSVHeader = + "Command, Date/Time, Elapsed Time, FPS, Bitrate, " + "Y PSNR, U PSNR, V PSNR, Global PSNR, SSIM, SSIM (dB), " + "I count, I ave-QP, I kbps, I-PSNR Y, I-PSNR U, I-PSNR V, I-SSIM (dB), " + "P count, P ave-QP, P kbps, P-PSNR Y, P-PSNR U, P-PSNR V, P-SSIM (dB), " + "B count, B ave-QP, B kbps, B-PSNR Y, B-PSNR U, B-PSNR V, B-SSIM (dB), " + "MaxCLL, MaxFALL, Version\n"; + x265_encoder *x265_encoder_open(x265_param *p) { if (!p) @@ -120,7 +127,7 @@ /* Try to open CSV file handle */ if (encoder->m_param->csvfn) { - encoder->m_param->csvfpt = x265_csvlog_open(*encoder->m_param, encoder->m_param->csvfn, encoder->m_param->csvLogLevel); + encoder->m_param->csvfpt = x265_csvlog_open(*encoder->m_param); if (!encoder->m_param->csvfpt) { x265_log(encoder->m_param, X265_LOG_ERROR, "Unable to open CSV log file <%s>, aborting\n", encoder->m_param->csvfn); @@ -280,7 +287,7 @@ *pi_nal = 0; if (numEncoded && encoder->m_param->csvLogLevel) - x265_csvlog_frame(encoder->m_param->csvfpt, *encoder->m_param, *pic_out, encoder->m_param->csvLogLevel); + x265_csvlog_frame(*encoder->m_param, *pic_out); if (numEncoded < 0) encoder->m_aborted = true; @@ -303,11 +310,8 @@ { Encoder *encoder = static_cast<Encoder*>(enc); x265_stats stats; - int padx = encoder->m_sps.conformanceWindow.rightOffset; - int pady = encoder->m_sps.conformanceWindow.bottomOffset; encoder->fetchStats(&stats, sizeof(stats)); - const x265_api * api = x265_api_get(0); - x265_csvlog_encode(encoder->m_param->csvfpt, api->version_str, *encoder->m_param, padx, pady, stats, encoder->m_param->csvLogLevel, argc, argv); + x265_csvlog_encode(enc, stats, argc, argv); } } @@ -436,6 +440,10 @@ &x265_encoder_ctu_info, &x265_get_slicetype_poc_and_scenecut, &x265_get_ref_frame_list, + &x265_csvlog_open, + &x265_csvlog_frame, + &x265_csvlog_encode, + &x265_dither_image, }; typedef const x265_api* (*api_get_func)(int bitDepth); @@ -630,4 +638,422 @@ return &libapi; } +FILE* x265_csvlog_open(const x265_param& param) +{ + FILE *csvfp = x265_fopen(param.csvfn, "r"); + if (csvfp) + { + /* file already exists, re-open for append */ + fclose(csvfp); + return x265_fopen(param.csvfn, "ab"); + } + else + { + /* new CSV file, write header */ + csvfp = x265_fopen(param.csvfn, "wb"); + if (csvfp) + { + if (param.csvLogLevel) + { + fprintf(csvfp, "Encode Order, Type, POC, QP, Bits, Scenecut, "); + if (param.csvLogLevel >= 2) + fprintf(csvfp, "I/P cost ratio, "); + if (param.rc.rateControlMode == X265_RC_CRF) + fprintf(csvfp, "RateFactor, "); + if (param.rc.vbvBufferSize) + fprintf(csvfp, "BufferFill, "); + if (param.bEnablePsnr) + fprintf(csvfp, "Y PSNR, U PSNR, V PSNR, YUV PSNR, "); + if (param.bEnableSsim) + fprintf(csvfp, "SSIM, SSIM(dB), "); + fprintf(csvfp, "Latency, "); + fprintf(csvfp, "List 0, List 1"); + uint32_t size = param.maxCUSize; + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Intra %dx%d DC, Intra %dx%d Planar, Intra %dx%d Ang", size, size, size, size, size, size); + size /= 2; + } + fprintf(csvfp, ", 4x4"); + size = param.maxCUSize; + if (param.bEnableRectInter) + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Inter %dx%d, Inter %dx%d (Rect)", size, size, size, size); + if (param.bEnableAMP) + fprintf(csvfp, ", Inter %dx%d (Amp)", size, size); + size /= 2; + } + } + else + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Inter %dx%d", size, size); + size /= 2; + } + } + size = param.maxCUSize; + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Skip %dx%d", size, size); + size /= 2; + } + size = param.maxCUSize; + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(csvfp, ", Merge %dx%d", size, size); + size /= 2; + } + + if (param.csvLogLevel >= 2) + { + fprintf(csvfp, ", Avg Luma Distortion, Avg Chroma Distortion, Avg psyEnergy, Avg Residual Energy," + " Min Luma Level, Max Luma Level, Avg Luma Level"); + + if (param.internalCsp != X265_CSP_I400) + fprintf(csvfp, ", Min Cb Level, Max Cb Level, Avg Cb Level, Min Cr Level, Max Cr Level, Avg Cr Level"); + + /* PU statistics */ + size = param.maxCUSize; + for (uint32_t i = 0; i< param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) + { + fprintf(csvfp, ", Intra %dx%d", size, size); + fprintf(csvfp, ", Skip %dx%d", size, size); + fprintf(csvfp, ", AMP %d", size); + fprintf(csvfp, ", Inter %dx%d", size, size); + fprintf(csvfp, ", Merge %dx%d", size, size); + fprintf(csvfp, ", Inter %dx%d", size, size / 2); + fprintf(csvfp, ", Merge %dx%d", size, size / 2); + fprintf(csvfp, ", Inter %dx%d", size / 2, size); + fprintf(csvfp, ", Merge %dx%d", size / 2, size); + size /= 2; + } + + if ((uint32_t)g_log2Size[param.minCUSize] == 3) + fprintf(csvfp, ", 4x4"); + + /* detailed performance statistics */ + fprintf(csvfp, ", DecideWait (ms), Row0Wait (ms), Wall time (ms), Ref Wait Wall (ms), Total CTU time (ms)," + "Stall Time (ms), Total frame time (ms), Avg WPP, Row Blocks"); + } + fprintf(csvfp, "\n"); + } + else + fputs(summaryCSVHeader, csvfp); + } + return csvfp; + } +} + +// per frame CSV logging +void x265_csvlog_frame(const x265_param& param, const x265_picture& pic) +{ + if (!param.csvfpt) + return; + + const x265_frame_stats* frameStats = &pic.frameData; + fprintf(param.csvfpt, "%d, %c-SLICE, %4d, %2.2lf, %10d, %d,", frameStats->encoderOrder, frameStats->sliceType, frameStats->poc, + frameStats->qp, (int)frameStats->bits, frameStats->bScenecut); + if (param.csvLogLevel >= 2) + fprintf(param.csvfpt, "%.2f,", frameStats->ipCostRatio); + if (param.rc.rateControlMode == X265_RC_CRF) + fprintf(param.csvfpt, "%.3lf,", frameStats->rateFactor); + if (param.rc.vbvBufferSize) + fprintf(param.csvfpt, "%.3lf,", frameStats->bufferFill); + if (param.bEnablePsnr) + fprintf(param.csvfpt, "%.3lf, %.3lf, %.3lf, %.3lf,", frameStats->psnrY, frameStats->psnrU, frameStats->psnrV, frameStats->psnr); + if (param.bEnableSsim) + fprintf(param.csvfpt, " %.6f, %6.3f,", frameStats->ssim, x265_ssim2dB(frameStats->ssim)); + fprintf(param.csvfpt, "%d, ", frameStats->frameLatency); + if (frameStats->sliceType == 'I' || frameStats->sliceType == 'i') + fputs(" -, -,", param.csvfpt); + else + { + int i = 0; + while (frameStats->list0POC[i] != -1) + fprintf(param.csvfpt, "%d ", frameStats->list0POC[i++]); + fprintf(param.csvfpt, ","); + if (frameStats->sliceType != 'P') + { + i = 0; + while (frameStats->list1POC[i] != -1) + fprintf(param.csvfpt, "%d ", frameStats->list1POC[i++]); + fprintf(param.csvfpt, ","); + } + else + fputs(" -,", param.csvfpt); + } + + if (param.csvLogLevel) + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, "%5.2lf%%, %5.2lf%%, %5.2lf%%,", frameStats->cuStats.percentIntraDistribution[depth][0], + frameStats->cuStats.percentIntraDistribution[depth][1], + frameStats->cuStats.percentIntraDistribution[depth][2]); + fprintf(param.csvfpt, "%5.2lf%%", frameStats->cuStats.percentIntraNxN); + if (param.bEnableRectInter) + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + { + fprintf(param.csvfpt, ", %5.2lf%%, %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0], + frameStats->cuStats.percentInterDistribution[depth][1]); + if (param.bEnableAMP) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][2]); + } + } + else + { + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0]); + } + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentSkipCu[depth]); + for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) + fprintf(param.csvfpt, ", %5.2lf%%", frameStats->cuStats.percentMergeCu[depth]); + } + + if (param.csvLogLevel >= 2) + { + fprintf(param.csvfpt, ", %.2lf, %.2lf, %.2lf, %.2lf ", frameStats->avgLumaDistortion, + frameStats->avgChromaDistortion, + frameStats->avgPsyEnergy, + frameStats->avgResEnergy); + + fprintf(param.csvfpt, ", %d, %d, %.2lf", frameStats->minLumaLevel, frameStats->maxLumaLevel, frameStats->avgLumaLevel); + + if (param.internalCsp != X265_CSP_I400) + { + fprintf(param.csvfpt, ", %d, %d, %.2lf", frameStats->minChromaULevel, frameStats->maxChromaULevel, frameStats->avgChromaULevel); + fprintf(param.csvfpt, ", %d, %d, %.2lf", frameStats->minChromaVLevel, frameStats->maxChromaVLevel, frameStats->avgChromaVLevel); + } + + for (uint32_t i = 0; i < param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) + { + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentIntraPu[i]); + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentSkipPu[i]); + fprintf(param.csvfpt, ",%.2lf%%", frameStats->puStats.percentAmpPu[i]); + for (uint32_t j = 0; j < 3; j++) + { + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentInterPu[i][j]); + fprintf(param.csvfpt, ", %.2lf%%", frameStats->puStats.percentMergePu[i][j]); + } + } + if ((uint32_t)g_log2Size[param.minCUSize] == 3) + fprintf(param.csvfpt, ",%.2lf%%", frameStats->puStats.percentNxN); + + fprintf(param.csvfpt, ", %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf,", frameStats->decideWaitTime, frameStats->row0WaitTime, + frameStats->wallTime, frameStats->refWaitWallTime, + frameStats->totalCTUTime, frameStats->stallTime, + frameStats->totalFrameTime); + + fprintf(param.csvfpt, " %.3lf, %d", frameStats->avgWPP, frameStats->countRowBlocks); + } + fprintf(param.csvfpt, "\n"); + fflush(stderr); +} + +void x265_csvlog_encode(x265_encoder *enc, const x265_stats& stats, int argc, char** argv) +{ + if (enc) + { + Encoder *encoder = static_cast<Encoder*>(enc); + int padx = encoder->m_sps.conformanceWindow.rightOffset; + int pady = encoder->m_sps.conformanceWindow.bottomOffset; + const x265_api * api = x265_api_get(0); + + if (!encoder->m_param->csvfpt) + return; + + if (encoder->m_param->csvLogLevel) + { + // adding summary to a per-frame csv log file, so it needs a summary header + fprintf(encoder->m_param->csvfpt, "\nSummary\n"); + fputs(summaryCSVHeader, encoder->m_param->csvfpt); + } + + // CLI arguments or other + if (argc) + { + fputc('"', encoder->m_param->csvfpt); + for (int i = 1; i < argc; i++) + { + fputc(' ', encoder->m_param->csvfpt); + fputs(argv[i], encoder->m_param->csvfpt); + } + fputc('"', encoder->m_param->csvfpt); + } + else + { + const x265_param* paramTemp = encoder->m_param; + char *opts = x265_param2string((x265_param*)paramTemp, padx, pady); + if (opts) + { + fputc('"', encoder->m_param->csvfpt); + fputs(opts, encoder->m_param->csvfpt); + fputc('"', encoder->m_param->csvfpt); + } + } + + // current date and time + time_t now; + struct tm* timeinfo; + time(&now); + timeinfo = localtime(&now); + char buffer[200]; + strftime(buffer, 128, "%c", timeinfo); + fprintf(encoder->m_param->csvfpt, ", %s, ", buffer); + + // elapsed time, fps, bitrate + fprintf(encoder->m_param->csvfpt, "%.2f, %.2f, %.2f,", + stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate); + + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf, %.3lf,", + stats.globalPsnrY / stats.encodedPictureCount, stats.globalPsnrU / stats.encodedPictureCount, + stats.globalPsnrV / stats.encodedPictureCount, stats.globalPsnr); + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.6f, %6.3f,", stats.globalSsim, x265_ssim2dB(stats.globalSsim)); + else + fprintf(encoder->m_param->csvfpt, " -, -,"); + + if (stats.statsI.numPics) + { + fprintf(encoder->m_param->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats.statsI.numPics, stats.statsI.avgQp, stats.statsI.bitrate); + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf,", stats.statsI.psnrY, stats.statsI.psnrU, stats.statsI.psnrV); + else + fprintf(encoder->m_param->csvfpt, " -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.3lf,", stats.statsI.ssim); + else + fprintf(encoder->m_param->csvfpt, " -,"); + } + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -, -, -, -,"); + + if (stats.statsP.numPics) + { + fprintf(encoder->m_param->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats.statsP.numPics, stats.statsP.avgQp, stats.statsP.bitrate); + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf,", stats.statsP.psnrY, stats.statsP.psnrU, stats.statsP.psnrV); + else + fprintf(encoder->m_param->csvfpt, " -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.3lf,", stats.statsP.ssim); + else + fprintf(encoder->m_param->csvfpt, " -,"); + } + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -, -, -, -,"); + + if (stats.statsB.numPics) + { + fprintf(encoder->m_param->csvfpt, " %-6u, %2.2lf, %-8.2lf,", stats.statsB.numPics, stats.statsB.avgQp, stats.statsB.bitrate); + if (encoder->m_param->bEnablePsnr) + fprintf(encoder->m_param->csvfpt, " %.3lf, %.3lf, %.3lf,", stats.statsB.psnrY, stats.statsB.psnrU, stats.statsB.psnrV); + else + fprintf(encoder->m_param->csvfpt, " -, -, -,"); + if (encoder->m_param->bEnableSsim) + fprintf(encoder->m_param->csvfpt, " %.3lf,", stats.statsB.ssim); + else + fprintf(encoder->m_param->csvfpt, " -,"); + } + else + fprintf(encoder->m_param->csvfpt, " -, -, -, -, -, -, -,"); + + fprintf(encoder->m_param->csvfpt, " %-6u, %-6u, %s\n", stats.maxCLL, stats.maxFALL, api->version_str); + } +} + +/* The dithering algorithm is based on Sierra-2-4A error diffusion. + * We convert planes in place (without allocating a new buffer). */ +static void ditherPlane(uint16_t *src, int srcStride, int width, int height, int16_t *errors, int bitDepth) +{ + const int lShift = 16 - bitDepth; + const int rShift = 16 - bitDepth + 2; + const int half = (1 << (16 - bitDepth + 1)); + const int pixelMax = (1 << bitDepth) - 1; + + memset(errors, 0, (width + 1) * sizeof(int16_t)); + + if (bitDepth == 8) + { + for (int y = 0; y < height; y++, src += srcStride) + { + uint8_t* dst = (uint8_t *)src; + int16_t err = 0; + for (int x = 0; x < width; x++) + { + err = err * 2 + errors[x] + errors[x + 1]; + int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); + errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); + dst[x] = (uint8_t)tmpDst; + } + } + } + else + { + for (int y = 0; y < height; y++, src += srcStride) + { + int16_t err = 0; + for (int x = 0; x < width; x++) + { + err = err * 2 + errors[x] + errors[x + 1]; + int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); + errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); + src[x] = (uint16_t)tmpDst; + } + } + } +} + +void x265_dither_image(x265_picture& picIn, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth) +{ + const x265_api* api = x265_api_get(0); + + if (sizeof(x265_picture) != api->sizeof_picture) + { + fprintf(stderr, "extras [error]: structure size skew, unable to dither\n"); + return; + } + + if (picIn.bitDepth <= 8) + { + fprintf(stderr, "extras [error]: dither support enabled only for input bitdepth > 8\n"); + return; + } + + if (picIn.bitDepth == bitDepth) + { + fprintf(stderr, "extras[error]: dither support enabled only if encoder depth is different from picture depth\n"); + return; + } + + /* This portion of code is from readFrame in x264. */ + for (int i = 0; i < x265_cli_csps[picIn.colorSpace].planes; i++) + { + if (picIn.bitDepth < 16) + { + /* upconvert non 16bit high depth planes to 16bit */ + uint16_t *plane = (uint16_t*)picIn.planes[i]; + uint32_t pixelCount = x265_picturePlaneSize(picIn.colorSpace, picWidth, picHeight, i); + int lShift = 16 - picIn.bitDepth; + + /* This loop assumes width is equal to stride which + * happens to be true for file reader outputs */ + for (uint32_t j = 0; j < pixelCount; j++) + plane[j] = plane[j] << lShift; + } + + int height = (int)(picHeight >> x265_cli_csps[picIn.colorSpace].height[i]); + int width = (int)(picWidth >> x265_cli_csps[picIn.colorSpace].width[i]); + + ditherPlane(((uint16_t*)picIn.planes[i]), picIn.stride[i] / 2, width, height, errorBuf, bitDepth); + } +} + } /* end namespace or extern "C" */ diff -r bd438ce10843 -r 563cbe1f4a21 source/x265-extras.cpp --- a/source/x265-extras.cpp Wed Nov 08 16:18:29 2017 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,447 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2013-2017 MulticoreWare, Inc - * - * Authors: Steve Borho <st...@borho.org> - * Selvakumar Nithiyaruban <selvaku...@multicorewareinc.com> - * Divya Manivannan <di...@multicorewareinc.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. - * - * This program is also available under a commercial proprietary license. - * For more information, contact us at license @ x265.com. - *****************************************************************************/ - -#include "x265.h" -#include "x265-extras.h" -#include "param.h" -#include "common.h" - -using namespace X265_NS; - -static const char* summaryCSVHeader = - "Command, Date/Time, Elapsed Time, FPS, Bitrate, " - "Y PSNR, U PSNR, V PSNR, Global PSNR, SSIM, SSIM (dB), " - "I count, I ave-QP, I kbps, I-PSNR Y, I-PSNR U, I-PSNR V, I-SSIM (dB), " - "P count, P ave-QP, P kbps, P-PSNR Y, P-PSNR U, P-PSNR V, P-SSIM (dB), " - "B count, B ave-QP, B kbps, B-PSNR Y, B-PSNR U, B-PSNR V, B-SSIM (dB), " - "MaxCLL, MaxFALL, Version\n"; - -FILE* x265_csvlog_open(const x265_param& param, const char* fname, int level) -{ - FILE *csvfp = x265_fopen(fname, "r"); - if (csvfp) - { - /* file already exists, re-open for append */ - fclose(csvfp); - return x265_fopen(fname, "ab"); - } - else - { - /* new CSV file, write header */ - csvfp = x265_fopen(fname, "wb"); - if (csvfp) - { - if (level) - { - fprintf(csvfp, "Encode Order, Type, POC, QP, Bits, Scenecut, "); - if (level >= 2) - fprintf(csvfp, "I/P cost ratio, "); - if (param.rc.rateControlMode == X265_RC_CRF) - fprintf(csvfp, "RateFactor, "); - if (param.rc.vbvBufferSize) - fprintf(csvfp, "BufferFill, "); - if (param.bEnablePsnr) - fprintf(csvfp, "Y PSNR, U PSNR, V PSNR, YUV PSNR, "); - if (param.bEnableSsim) - fprintf(csvfp, "SSIM, SSIM(dB), "); - fprintf(csvfp, "Latency, "); - fprintf(csvfp, "List 0, List 1"); - uint32_t size = param.maxCUSize; - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Intra %dx%d DC, Intra %dx%d Planar, Intra %dx%d Ang", size, size, size, size, size, size); - size /= 2; - } - fprintf(csvfp, ", 4x4"); - size = param.maxCUSize; - if (param.bEnableRectInter) - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Inter %dx%d, Inter %dx%d (Rect)", size, size, size, size); - if (param.bEnableAMP) - fprintf(csvfp, ", Inter %dx%d (Amp)", size, size); - size /= 2; - } - } - else - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Inter %dx%d", size, size); - size /= 2; - } - } - size = param.maxCUSize; - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Skip %dx%d", size, size); - size /= 2; - } - size = param.maxCUSize; - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", Merge %dx%d", size, size); - size /= 2; - } - - if (level >= 2) - { - fprintf(csvfp, ", Avg Luma Distortion, Avg Chroma Distortion, Avg psyEnergy, Avg Residual Energy," - " Min Luma Level, Max Luma Level, Avg Luma Level"); - - if (param.internalCsp != X265_CSP_I400) - fprintf(csvfp, ", Min Cb Level, Max Cb Level, Avg Cb Level, Min Cr Level, Max Cr Level, Avg Cr Level"); - - /* PU statistics */ - size = param.maxCUSize; - for (uint32_t i = 0; i< param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) - { - fprintf(csvfp, ", Intra %dx%d", size, size); - fprintf(csvfp, ", Skip %dx%d", size, size); - fprintf(csvfp, ", AMP %d", size); - fprintf(csvfp, ", Inter %dx%d", size, size); - fprintf(csvfp, ", Merge %dx%d", size, size); - fprintf(csvfp, ", Inter %dx%d", size, size / 2); - fprintf(csvfp, ", Merge %dx%d", size, size / 2); - fprintf(csvfp, ", Inter %dx%d", size / 2, size); - fprintf(csvfp, ", Merge %dx%d", size / 2, size); - size /= 2; - } - - if ((uint32_t)g_log2Size[param.minCUSize] == 3) - fprintf(csvfp, ", 4x4"); - - /* detailed performance statistics */ - fprintf(csvfp, ", DecideWait (ms), Row0Wait (ms), Wall time (ms), Ref Wait Wall (ms), Total CTU time (ms)," - "Stall Time (ms), Total frame time (ms), Avg WPP, Row Blocks"); - } - fprintf(csvfp, "\n"); - } - else - fputs(summaryCSVHeader, csvfp); - } - return csvfp; - } -} - -// per frame CSV logging -void x265_csvlog_frame(FILE* csvfp, const x265_param& param, const x265_picture& pic, int level) -{ - if (!csvfp) - return; - - const x265_frame_stats* frameStats = &pic.frameData; - fprintf(csvfp, "%d, %c-SLICE, %4d, %2.2lf, %10d, %d,", frameStats->encoderOrder, frameStats->sliceType, frameStats->poc, - frameStats->qp, (int)frameStats->bits, frameStats->bScenecut); - if (level >= 2) - fprintf(csvfp, "%.2f,", frameStats->ipCostRatio); - if (param.rc.rateControlMode == X265_RC_CRF) - fprintf(csvfp, "%.3lf,", frameStats->rateFactor); - if (param.rc.vbvBufferSize) - fprintf(csvfp, "%.3lf,", frameStats->bufferFill); - if (param.bEnablePsnr) - fprintf(csvfp, "%.3lf, %.3lf, %.3lf, %.3lf,", frameStats->psnrY, frameStats->psnrU, frameStats->psnrV, frameStats->psnr); - if (param.bEnableSsim) - fprintf(csvfp, " %.6f, %6.3f,", frameStats->ssim, x265_ssim2dB(frameStats->ssim)); - fprintf(csvfp, "%d, ", frameStats->frameLatency); - if (frameStats->sliceType == 'I' || frameStats->sliceType == 'i') - fputs(" -, -,", csvfp); - else - { - int i = 0; - while (frameStats->list0POC[i] != -1) - fprintf(csvfp, "%d ", frameStats->list0POC[i++]); - fprintf(csvfp, ","); - if (frameStats->sliceType != 'P') - { - i = 0; - while (frameStats->list1POC[i] != -1) - fprintf(csvfp, "%d ", frameStats->list1POC[i++]); - fprintf(csvfp, ","); - } - else - fputs(" -,", csvfp); - } - - if (level) - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, "%5.2lf%%, %5.2lf%%, %5.2lf%%,", frameStats->cuStats.percentIntraDistribution[depth][0], - frameStats->cuStats.percentIntraDistribution[depth][1], - frameStats->cuStats.percentIntraDistribution[depth][2]); - fprintf(csvfp, "%5.2lf%%", frameStats->cuStats.percentIntraNxN); - if (param.bEnableRectInter) - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - { - fprintf(csvfp, ", %5.2lf%%, %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0], - frameStats->cuStats.percentInterDistribution[depth][1]); - if (param.bEnableAMP) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][2]); - } - } - else - { - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentInterDistribution[depth][0]); - } - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentSkipCu[depth]); - for (uint32_t depth = 0; depth <= param.maxCUDepth; depth++) - fprintf(csvfp, ", %5.2lf%%", frameStats->cuStats.percentMergeCu[depth]); - } - - if (level >= 2) - { - fprintf(csvfp, ", %.2lf, %.2lf, %.2lf, %.2lf ", frameStats->avgLumaDistortion, - frameStats->avgChromaDistortion, - frameStats->avgPsyEnergy, - frameStats->avgResEnergy); - - fprintf(csvfp, ", %d, %d, %.2lf", frameStats->minLumaLevel, frameStats->maxLumaLevel, frameStats->avgLumaLevel); - - if (param.internalCsp != X265_CSP_I400) - { - fprintf(csvfp, ", %d, %d, %.2lf", frameStats->minChromaULevel, frameStats->maxChromaULevel, frameStats->avgChromaULevel); - fprintf(csvfp, ", %d, %d, %.2lf", frameStats->minChromaVLevel, frameStats->maxChromaVLevel, frameStats->avgChromaVLevel); - } - - for (uint32_t i = 0; i < param.maxLog2CUSize - (uint32_t)g_log2Size[param.minCUSize] + 1; i++) - { - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentIntraPu[i]); - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentSkipPu[i]); - fprintf(csvfp, ",%.2lf%%", frameStats->puStats.percentAmpPu[i]); - for (uint32_t j = 0; j < 3; j++) - { - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentInterPu[i][j]); - fprintf(csvfp, ", %.2lf%%", frameStats->puStats.percentMergePu[i][j]); - } - } - if ((uint32_t)g_log2Size[param.minCUSize] == 3) - fprintf(csvfp, ",%.2lf%%", frameStats->puStats.percentNxN); - - fprintf(csvfp, ", %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf, %.1lf,", frameStats->decideWaitTime, frameStats->row0WaitTime, - frameStats->wallTime, frameStats->refWaitWallTime, - frameStats->totalCTUTime, frameStats->stallTime, - frameStats->totalFrameTime); - - fprintf(csvfp, " %.3lf, %d", frameStats->avgWPP, frameStats->countRowBlocks); - } - fprintf(csvfp, "\n"); - fflush(stderr); -} - -void x265_csvlog_encode(FILE* csvfp, const char* version, const x265_param& param, int padx, int pady, const x265_stats& stats, int level, int argc, char** argv) -{ - if (!csvfp) - return; - - if (level) - { - // adding summary to a per-frame csv log file, so it needs a summary header - fprintf(csvfp, "\nSummary\n"); - fputs(summaryCSVHeader, csvfp); - } - - // CLI arguments or other - if (argc) - { - fputc('"', csvfp); - for (int i = 1; i < argc; i++) - { - fputc(' ', csvfp); - fputs(argv[i], csvfp); - } - fputc('"', csvfp); - } - else - { - const x265_param* paramTemp = ¶m; - char *opts = x265_param2string((x265_param*)paramTemp, padx, pady); - if (opts) - { - fputc('"', csvfp); - fputs(opts, csvfp); - fputc('"', csvfp); - } - } - - // current date and time - time_t now; - struct tm* timeinfo; - time(&now); - timeinfo = localtime(&now); - char buffer[200]; - strftime(buffer, 128, "%c", timeinfo); - fprintf(csvfp, ", %s, ", buffer); - - // elapsed time, fps, bitrate - fprintf(csvfp, "%.2f, %.2f, %.2f,", - stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate); - - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf, %.3lf,", - stats.globalPsnrY / stats.encodedPictureCount, stats.globalPsnrU / stats.encodedPictureCount, - stats.globalPsnrV / stats.encodedPictureCount, stats.globalPsnr); - else - fprintf(csvfp, " -, -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.6f, %6.3f,", stats.globalSsim, x265_ssim2dB(stats.globalSsim)); - else - fprintf(csvfp, " -, -,"); - - if (stats.statsI.numPics) - { - fprintf(csvfp, " %-6u, %2.2lf, %-8.2lf,", stats.statsI.numPics, stats.statsI.avgQp, stats.statsI.bitrate); - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf,", stats.statsI.psnrY, stats.statsI.psnrU, stats.statsI.psnrV); - else - fprintf(csvfp, " -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.3lf,", stats.statsI.ssim); - else - fprintf(csvfp, " -,"); - } - else - fprintf(csvfp, " -, -, -, -, -, -, -,"); - - if (stats.statsP.numPics) - { - fprintf(csvfp, " %-6u, %2.2lf, %-8.2lf,", stats.statsP.numPics, stats.statsP.avgQp, stats.statsP.bitrate); - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf,", stats.statsP.psnrY, stats.statsP.psnrU, stats.statsP.psnrV); - else - fprintf(csvfp, " -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.3lf,", stats.statsP.ssim); - else - fprintf(csvfp, " -,"); - } - else - fprintf(csvfp, " -, -, -, -, -, -, -,"); - - if (stats.statsB.numPics) - { - fprintf(csvfp, " %-6u, %2.2lf, %-8.2lf,", stats.statsB.numPics, stats.statsB.avgQp, stats.statsB.bitrate); - if (param.bEnablePsnr) - fprintf(csvfp, " %.3lf, %.3lf, %.3lf,", stats.statsB.psnrY, stats.statsB.psnrU, stats.statsB.psnrV); - else - fprintf(csvfp, " -, -, -,"); - if (param.bEnableSsim) - fprintf(csvfp, " %.3lf,", stats.statsB.ssim); - else - fprintf(csvfp, " -,"); - } - else - fprintf(csvfp, " -, -, -, -, -, -, -,"); - - fprintf(csvfp, " %-6u, %-6u, %s\n", stats.maxCLL, stats.maxFALL, version); -} - -/* The dithering algorithm is based on Sierra-2-4A error diffusion. - * We convert planes in place (without allocating a new buffer). */ -static void ditherPlane(uint16_t *src, int srcStride, int width, int height, int16_t *errors, int bitDepth) -{ - const int lShift = 16 - bitDepth; - const int rShift = 16 - bitDepth + 2; - const int half = (1 << (16 - bitDepth + 1)); - const int pixelMax = (1 << bitDepth) - 1; - - memset(errors, 0, (width + 1) * sizeof(int16_t)); - - if (bitDepth == 8) - { - for (int y = 0; y < height; y++, src += srcStride) - { - uint8_t* dst = (uint8_t *)src; - int16_t err = 0; - for (int x = 0; x < width; x++) - { - err = err * 2 + errors[x] + errors[x + 1]; - int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); - errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); - dst[x] = (uint8_t)tmpDst; - } - } - } - else - { - for (int y = 0; y < height; y++, src += srcStride) - { - int16_t err = 0; - for (int x = 0; x < width; x++) - { - err = err * 2 + errors[x] + errors[x + 1]; - int tmpDst = x265_clip3(0, pixelMax, ((src[x] << 2) + err + half) >> rShift); - errors[x] = err = (int16_t)(src[x] - (tmpDst << lShift)); - src[x] = (uint16_t)tmpDst; - } - } - } -} - -void x265_dither_image(const x265_api& api, x265_picture& picIn, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth) -{ - if (sizeof(x265_picture) != api.sizeof_picture) - { - fprintf(stderr, "extras [error]: structure size skew, unable to dither\n"); - return; - } - - if (picIn.bitDepth <= 8) - { - fprintf(stderr, "extras [error]: dither support enabled only for input bitdepth > 8\n"); - return; - } - - if (picIn.bitDepth == bitDepth) - { - fprintf(stderr, "extras[error]: dither support enabled only if encoder depth is different from picture depth\n"); - return; - } - - /* This portion of code is from readFrame in x264. */ - for (int i = 0; i < x265_cli_csps[picIn.colorSpace].planes; i++) - { - if (picIn.bitDepth < 16) - { - /* upconvert non 16bit high depth planes to 16bit */ - uint16_t *plane = (uint16_t*)picIn.planes[i]; - uint32_t pixelCount = x265_picturePlaneSize(picIn.colorSpace, picWidth, picHeight, i); - int lShift = 16 - picIn.bitDepth; - - /* This loop assumes width is equal to stride which - * happens to be true for file reader outputs */ - for (uint32_t j = 0; j < pixelCount; j++) - plane[j] = plane[j] << lShift; - } - - int height = (int)(picHeight >> x265_cli_csps[picIn.colorSpace].height[i]); - int width = (int)(picWidth >> x265_cli_csps[picIn.colorSpace].width[i]); - - ditherPlane(((uint16_t*)picIn.planes[i]), picIn.stride[i] / 2, width, height, errorBuf, bitDepth); - } -} diff -r bd438ce10843 -r 563cbe1f4a21 source/x265-extras.h --- a/source/x265-extras.h Wed Nov 08 16:18:29 2017 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2013-2017 MulticoreWare, Inc - * - * Authors: Steve Borho <st...@borho.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA. - * - * This program is also available under a commercial proprietary license. - * For more information, contact us at license @ x265.com. - *****************************************************************************/ - -#ifndef X265_EXTRAS_H -#define X265_EXTRAS_H 1 - -#include "x265.h" - -#include <stdio.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#if _WIN32 -#define LIBAPI __declspec(dllexport) -#else -#define LIBAPI -#endif - -/* Open a CSV log file. On success it returns a file handle which must be passed - * to x265_csvlog_frame() and/or x265_csvlog_encode(). The file handle must be - * closed by the caller using fclose(). If level is 0, then no frame logging - * header is written to the file. This function will return NULL if it is unable - * to open the file for write or if it detects a structure size skew */ -LIBAPI FILE* x265_csvlog_open(const x265_param& param, const char* fname, int level); - -/* Log frame statistics to the CSV file handle. level should have been non-zero - * in the call to x265_csvlog_open() if this function is called. */ -LIBAPI void x265_csvlog_frame(FILE* csvfp, const x265_param& param, const x265_picture& pic, int level); - -/* Log final encode statistics to the CSV file handle. 'argc' and 'argv' are - * intended to be command line arguments passed to the encoder. Encode - * statistics should be queried from the encoder just prior to closing it. */ -LIBAPI void x265_csvlog_encode(FILE* csvfp, const char* version, const x265_param& param, int padx, int pady, const x265_stats& stats, int level, int argc, char** argv); - -/* In-place downshift from a bit-depth greater than 8 to a bit-depth of 8, using - * the residual bits to dither each row. */ -LIBAPI void x265_dither_image(const x265_api& api, x265_picture&, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth); - -#ifdef __cplusplus -} -#endif - -#endif diff -r bd438ce10843 -r 563cbe1f4a21 source/x265.cpp --- a/source/x265.cpp Wed Nov 08 16:18:29 2017 +0530 +++ b/source/x265.cpp Wed Nov 08 17:08:18 2017 +0530 @@ -26,7 +26,6 @@ #endif #include "x265.h" -#include "x265-extras.h" #include "x265cli.h" #include "input/input.h" @@ -639,7 +638,7 @@ { if (pic_in->bitDepth > param->internalBitDepth && cliopt.bDither) { - x265_dither_image(*api, *pic_in, cliopt.input->getWidth(), cliopt.input->getHeight(), errorBuf, param->internalBitDepth); + x265_dither_image(*pic_in, cliopt.input->getWidth(), cliopt.input->getHeight(), errorBuf, param->internalBitDepth); pic_in->bitDepth = param->internalBitDepth; } /* Overwrite PTS */ diff -r bd438ce10843 -r 563cbe1f4a21 source/x265.def.in --- a/source/x265.def.in Wed Nov 08 16:18:29 2017 +0530 +++ b/source/x265.def.in Wed Nov 08 17:08:18 2017 +0530 @@ -26,3 +26,7 @@ x265_encoder_ctu_info x265_get_slicetype_poc_and_scenecut x265_get_ref_frame_list +x265_csvlog_open +x265_csvlog_frame +x265_csvlog_encode +x265_dither_image diff -r bd438ce10843 -r 563cbe1f4a21 source/x265.h --- a/source/x265.h Wed Nov 08 16:18:29 2017 +0530 +++ b/source/x265.h Wed Nov 08 17:08:18 2017 +0530 @@ -1745,6 +1745,26 @@ void x265_cleanup(void); +/* Open a CSV log file. On success it returns a file handle which must be passed + * to x265_csvlog_frame() and/or x265_csvlog_encode(). The file handle must be + * closed by the caller using fclose(). If csv-loglevel is 0, then no frame logging + * header is written to the file. This function will return NULL if it is unable + * to open the file for write or if it detects a structure size skew */ +FILE* x265_csvlog_open(const x265_param& param); + +/* Log frame statistics to the CSV file handle. csv-loglevel should have been non-zero + * in the call to x265_csvlog_open() if this function is called. */ +void x265_csvlog_frame(const x265_param& param, const x265_picture& pic); + +/* Log final encode statistics to the CSV file handle. 'argc' and 'argv' are + * intended to be command line arguments passed to the encoder. Encode + * statistics should be queried from the encoder just prior to closing it. */ +void x265_csvlog_encode(x265_encoder *encoder, const x265_stats& stats, int argc, char** argv); + +/* In-place downshift from a bit-depth greater than 8 to a bit-depth of 8, using + * the residual bits to dither each row. */ +void x265_dither_image(x265_picture& pic, int picWidth, int picHeight, int16_t *errorBuf, int bitDepth); + #define X265_MAJOR_VERSION 1 /* === Multi-lib API === @@ -1792,6 +1812,10 @@ int (*encoder_ctu_info)(x265_encoder*, int, x265_ctu_info_t**); int (*get_slicetype_poc_and_scenecut)(x265_encoder*, int*, int*, int*); int (*get_ref_frame_list)(x265_encoder*, x265_picyuv**, x265_picyuv**, int, int); + FILE* (*csvlog_open)(const x265_param&); + void (*csvlog_frame)(const x265_param&, const x265_picture&); + void (*csvlog_encode)(x265_encoder*, const x265_stats&, int, char**); + void (*dither_image)(x265_picture&, int, int, int16_t*, int); /* add new pointers to the end, or increment X265_MAJOR_VERSION */ } x265_api;
_______________________________________________ x265-devel mailing list x265-devel@videolan.org https://mailman.videolan.org/listinfo/x265-devel