Mercurial > x265
changeset 12437:e50f803e26fb draft
Add support for Dolby Vision RPU muxing
author | Aruna Matheswaran <aruna@multicorewareinc.com> |
---|---|
date | Thu, 27 Sep 2018 14:16:15 +0530 |
parents | b748ee9f4465 |
children | 8eb57eb225d5 |
files | doc/reST/cli.rst source/CMakeLists.txt source/common/frame.cpp source/common/frame.h source/encoder/api.cpp source/encoder/encoder.cpp source/encoder/frameencoder.cpp source/encoder/nal.cpp source/encoder/sei.cpp source/x265.cpp source/x265.h source/x265cli.h |
diffstat | 12 files changed, 148 insertions(+-), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/reST/cli.rst Fri Sep 28 10:45:23 2018 +0530 +++ b/doc/reST/cli.rst Thu Sep 27 14:16:15 2018 +0530 @@ -2208,6 +2208,15 @@ Bitstream options Currently only profile 5 enabled, Default 0 (disabled) +.. option:: --dolby-vision-rpu + + File containing Dolby Vision RPU metadata. If given, x265's Dolby Vision + metadata parser will fill the RPU field of input pictures with the metadata + read from the file. The library will interleave access units with RPUs in the + bitstream. Default NULL (disabled). + + **CLI ONLY** + .. option:: --info, --no-info Emit an informational SEI with the stream headers which describes
--- a/source/CMakeLists.txt Fri Sep 28 10:45:23 2018 +0530 +++ b/source/CMakeLists.txt Thu Sep 27 14:16:15 2018 +0530 @@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CP 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 166) +set(X265_BUILD 167) configure_file("${PROJECT_SOURCE_DIR}/x265.def.in" "${PROJECT_BINARY_DIR}/x265.def") configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
--- a/source/common/frame.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/common/frame.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -44,6 +44,8 @@ Frame::Frame() m_param = NULL; m_userSEI.numPayloads = 0; m_userSEI.payloads = NULL; + m_rpu.payloadSize = 0; + m_rpu.payload = NULL; memset(&m_lowres, 0, sizeof(m_lowres)); m_rcData = NULL; m_encodeStartTime = 0;
--- a/source/common/frame.h Fri Sep 28 10:45:23 2018 +0530 +++ b/source/common/frame.h Thu Sep 27 14:16:15 2018 +0530 @@ -98,6 +98,7 @@ public: float* m_quantOffsets; // points to quantOffsets in x265_picture x265_sei m_userSEI; + x265_dolby_vision_rpu m_rpu; /* Frame Parallelism - notification between FrameEncoders of available motion reference rows */ ThreadSafeInteger* m_reconRowFlag; // flag of CTU rows completely reconstructed and extended for motion reference
--- a/source/encoder/api.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/encoder/api.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -598,6 +598,8 @@ void x265_picture_init(x265_param *param pic->quantOffsets = NULL; pic->userSEI.payloads = NULL; pic->userSEI.numPayloads = 0; + pic->rpu.payloadSize = 0; + pic->rpu.payload = NULL; if ((param->analysisSave || param->analysisLoad) || (param->bMVType == AVC_INFO)) { @@ -612,6 +614,8 @@ void x265_picture_init(x265_param *param void x265_picture_free(x265_picture *p) { + if (p->rpu.payload) + X265_FREE(p->rpu.payload); return x265_free(p); }
--- a/source/encoder/encoder.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/encoder/encoder.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -1075,6 +1075,14 @@ int Encoder::encode(const x265_picture* copyUserSEIMessages(inFrame, pic_in); + /*Copy Dolby Vision RPU from pic_in to frame*/ + if (pic_in->rpu.payloadSize) + { + inFrame->m_rpu.payloadSize = pic_in->rpu.payloadSize; + inFrame->m_rpu.payload = new uint8_t[pic_in->rpu.payloadSize]; + memcpy(inFrame->m_rpu.payload, pic_in->rpu.payload, pic_in->rpu.payloadSize); + } + if (pic_in->quantOffsets != NULL) { int cuCount; @@ -2362,6 +2370,13 @@ void Encoder::getStreamHeaders(NALList& { sbacCoder.setBitstream(&bs); + if (m_param->dolbyProfile && !m_param->bRepeatHeaders) + { + bs.resetBits(); + bs.write(0x10, 8); + list.serialize(NAL_UNIT_ACCESS_UNIT_DELIMITER, bs); + } + /* headers for start of bitstream */ bs.resetBits(); sbacCoder.codeVPS(m_vps);
--- a/source/encoder/frameencoder.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/encoder/frameencoder.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -1063,6 +1063,14 @@ void FrameEncoder::compressFrame() m_accessUnitBits = bytes << 3; } + if (m_frame->m_rpu.payloadSize) + { + m_bs.resetBits(); + for (int i = 0; i < m_frame->m_rpu.payloadSize; i++) + m_bs.write(m_frame->m_rpu.payload[i], 8); + m_nalList.serialize(NAL_UNIT_UNSPECIFIED, m_bs); + } + m_endCompressTime = x265_mdate(); /* Decrement referenced frame reference counts, allow them to be recycled */
--- a/source/encoder/nal.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/encoder/nal.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -97,7 +97,7 @@ void NALList::serialize(NalUnitType nalU /* Will write size later */ bytes += 4; } - else if (!m_numNal || nalUnitType == NAL_UNIT_VPS || nalUnitType == NAL_UNIT_SPS || nalUnitType == NAL_UNIT_PPS) + else if (!m_numNal || nalUnitType == NAL_UNIT_VPS || nalUnitType == NAL_UNIT_SPS || nalUnitType == NAL_UNIT_PPS || nalUnitType == NAL_UNIT_UNSPECIFIED) { memcpy(out, startCodePrefix, 4); bytes += 4; @@ -124,7 +124,7 @@ void NALList::serialize(NalUnitType nalU * - 0x000002 */ for (uint32_t i = 0; i < payloadSize; i++) { - if (i > 2 && !out[bytes - 2] && !out[bytes - 3] && out[bytes - 1] <= 0x03) + if (i > 2 && !out[bytes - 2] && !out[bytes - 3] && out[bytes - 1] <= 0x03 && nalUnitType != NAL_UNIT_UNSPECIFIED) { /* inject 0x03 to prevent emulating a start code */ out[bytes] = out[bytes - 1];
--- a/source/encoder/sei.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/encoder/sei.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -66,7 +66,8 @@ void SEI::writeSEImessages(Bitstream& bs if (!isNested) { - bs.writeByteAlignment(); + if (nalUnitType != NAL_UNIT_UNSPECIFIED) + bs.writeByteAlignment(); list.serialize(nalUnitType, bs); } }
--- a/source/x265.cpp Fri Sep 28 10:45:23 2018 +0530 +++ b/source/x265.cpp Thu Sep 27 14:16:15 2018 +0530 @@ -65,6 +65,8 @@ static void sigint_handler(int) { b_ctrl_c = 1; } +#define START_CODE 0x00000001 +#define START_CODE_BYTES 4 struct CLIOptions { @@ -72,6 +74,7 @@ struct CLIOptions ReconFile* recon; OutputFile* output; FILE* qpfile; + FILE* dolbyVisionRpu; /* File containing Dolby Vision BL RPU metadata */ const char* reconPlayCmd; const x265_api* api; x265_param* param; @@ -94,6 +97,7 @@ struct CLIOptions recon = NULL; output = NULL; qpfile = NULL; + dolbyVisionRpu = NULL; reconPlayCmd = NULL; api = NULL; param = NULL; @@ -124,6 +128,9 @@ void CLIOptions::destroy() if (qpfile) fclose(qpfile); qpfile = NULL; + if (dolbyVisionRpu) + fclose(dolbyVisionRpu); + dolbyVisionRpu = NULL; if (output) output->release(); output = NULL; @@ -311,6 +318,15 @@ bool CLIOptions::parse(int argc, char ** if (!this->qpfile) x265_log_file(param, X265_LOG_ERROR, "%s qpfile not found or error in opening qp file\n", optarg); } + OPT("dolby-vision-rpu") + { + this->dolbyVisionRpu = x265_fopen(optarg, "rb"); + if (!this->dolbyVisionRpu) + { + x265_log_file(param, X265_LOG_ERROR, "Dolby Vision RPU metadata file %s not found or error in opening file\n", optarg); + return true; + } + } OPT("fullhelp") { param->logLevel = X265_LOG_FULL; @@ -552,6 +568,59 @@ static int get_argv_utf8(int *argc_ptr, } #endif +/* Parse the RPU file and extract the RPU corresponding to the current picture + * and fill the rpu field of the input picture */ +static int rpuParser(x265_picture * pic, FILE * ptr) +{ + uint8_t byte; + uint32_t code = 0; + int bytesRead = 0; + pic->rpu.payloadSize = 0; + + if (!pic->pts) + { + while (bytesRead++ < 4 && fread(&byte, sizeof(uint8_t), 1, ptr)) + code = (code << 8) | byte; + + if (code != START_CODE) + { + x265_log(NULL, X265_LOG_ERROR, "Invalid Dolby Vision RPU startcode in POC %d\n", pic->pts); + return 1; + } + } + + bytesRead = 0; + while (fread(&byte, sizeof(uint8_t), 1, ptr)) + { + code = (code << 8) | byte; + if (bytesRead++ < 3) + continue; + if (bytesRead >= 1024) + { + x265_log(NULL, X265_LOG_ERROR, "Invalid Dolby Vision RPU size in POC %d\n", pic->pts); + return 1; + } + + if (code != START_CODE) + pic->rpu.payload[pic->rpu.payloadSize++] = (code >> (3 * 8)) & 0xFF; + else + return 0; + } + + int ShiftBytes = START_CODE_BYTES - (bytesRead - pic->rpu.payloadSize); + int bytesLeft = bytesRead - pic->rpu.payloadSize; + code = (code << ShiftBytes * 8); + for (int i = 0; i < bytesLeft; i++) + { + pic->rpu.payload[pic->rpu.payloadSize++] = (code >> (3 * 8)) & 0xFF; + code = (code << 8); + } + if (!pic->rpu.payloadSize) + x265_log(NULL, X265_LOG_WARNING, "Dolby Vision RPU not found for POC %d\n", pic->pts); + return 0; +} + + /* CLI return codes: * * 0 - encode successful @@ -630,8 +699,10 @@ int main(int argc, char **argv) x265_stats stats; uint32_t nal; int16_t *errorBuf = NULL; + bool bDolbyVisionRPU = false; int ret = 0; + if (!param->bRepeatHeaders) { if (api->encoder_headers(encoder, &p_nal, &nal) < 0) @@ -646,6 +717,13 @@ int main(int argc, char **argv) api->picture_init(param, pic_in); + if (param->dolbyProfile && cliopt.dolbyVisionRpu) + { + pic_in->rpu.payload = X265_MALLOC(uint8_t, 1024); + if (pic_in->rpu.payload) + bDolbyVisionRPU = true; + } + if (cliopt.bDither) { errorBuf = X265_MALLOC(int16_t, param->sourceWidth + 1); @@ -685,8 +763,13 @@ int main(int argc, char **argv) } /* Overwrite PTS */ pic_in->pts = pic_in->poc; + + if (bDolbyVisionRPU) + { + if (rpuParser(pic_in, cliopt.dolbyVisionRpu) > 0) + goto fail; + } } - int numEncoded = api->encoder_encode(encoder, &p_nal, &nal, pic_in, pic_recon); if (numEncoded < 0) { @@ -749,6 +832,13 @@ int main(int argc, char **argv) break; } + if (bDolbyVisionRPU) + { + if(fgetc(cliopt.dolbyVisionRpu) != EOF) + x265_log(NULL, X265_LOG_WARNING, "Dolby Vision RPU count is greater than frame count\n"); + x265_log(NULL, X265_LOG_INFO, "VES muxing with Dolby Vision RPU file successful\n"); + } + /* clear progress report */ if (cliopt.bProgress) fprintf(stderr, "%*s\r", 80, " ");
--- a/source/x265.h Fri Sep 28 10:45:23 2018 +0530 +++ b/source/x265.h Thu Sep 27 14:16:15 2018 +0530 @@ -81,6 +81,7 @@ typedef enum NAL_UNIT_FILLER_DATA, NAL_UNIT_PREFIX_SEI, NAL_UNIT_SUFFIX_SEI, + NAL_UNIT_UNSPECIFIED = 62, NAL_UNIT_INVALID = 64, } NalUnitType; @@ -360,6 +361,12 @@ typedef struct x265_sei x265_sei_payload *payloads; } x265_sei; +typedef struct x265_dolby_vision_rpu +{ + int payloadSize; + uint8_t* payload; +}x265_dolby_vision_rpu; + /* Used to pass pictures into the encoder, and to get picture data back out of * the encoder. The input and output semantics are different */ typedef struct x265_picture @@ -445,6 +452,9 @@ typedef struct x265_picture // pts is reordered in the order of encoding. int64_t reorderedPts; + + //Dolby Vision RPU metadata + x265_dolby_vision_rpu rpu; } x265_picture; typedef enum
--- a/source/x265cli.h Fri Sep 28 10:45:23 2018 +0530 +++ b/source/x265cli.h Thu Sep 27 14:16:15 2018 +0530 @@ -306,6 +306,7 @@ static const struct option long_options[ { "atc-sei", required_argument, NULL, 0 }, { "pic-struct", required_argument, NULL, 0 }, { "nalu-file", required_argument, NULL, 0 }, + { "dolby-vision-rpu", required_argument, NULL, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, @@ -357,6 +358,8 @@ static void showHelp(x265_param *param) H0(" --[no-]dhdr10-opt Insert tone mapping SEI only for IDR frames and when the tone mapping information changes. Default disabled\n"); #endif H0(" --dolby-vision-profile <float|integer> Specifies Dolby Vision profile ID. Currently only profile 5 enabled. Specified as '5' or '50'. Default 0 (disabled).\n"); + H0(" --dolby-vision-rpu <filename> File containing Dolby Vision RPU metadata.\n" + " If given, x265's Dolby Vision metadata parser will fill the RPU field of input pictures with the metadata read from the file. Default NULL(disabled).\n"); H0(" --nalu-file <filename> Text file containing SEI messages in the following format : <POC><space><PREFIX><space><NAL UNIT TYPE>/<SEI TYPE><space><SEI Payload>\n"); H0("-f/--frames <integer> Maximum number of frames to encode. Default all\n"); H0(" --seek <integer> First frame to encode\n");