百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

FFmpeg 7.0 flv支持hdr ffmpeg 4.4

csdh11 2024-12-23 09:26 28 浏览

本文为转载,原作者:小新快跑/音视频小话

自从ffmpeg6.0应用enhance rtmp支持h265/av1的flv格式后,7.0迎来了flv的hdr能力。本文介绍ffmpeg7.0如何支持hdr in flv。

enhance rtmp关于hdr

文档[enhance-rtmp-v2.md](https://github.com/veovera/enhanced-rtmp/blob/main/docs/enhanced/enhanced-rtmp-v2.md)中,metadata-frame章节规定相关支持HDR部分。

定义一个新的VideoPacketType.Metadata,其内容为AMF格式的metadata(AMF是一种简便的描述格式,非常节省内存),具体内部有HDR相关的colorInfo数据(每次hdr参数更新的时候,就发送该metadata)。


type ColorInfo = {

colorConfig: {

// number of bits used to record the color channels for each pixel

bitDepth:                 number, // SHOULD be 8, 10 or 12



//

// colorPrimaries, transferCharacteristics and matrixCoefficients are defined

// in ISO/IEC 23091-4/ITU-T H.273. The values are an index into

// respective tables which are described in “Colour primaries”,

// "Transfer characteristics" and "Matrix coefficients" sections.

// It is RECOMMENDED to provide these values.

//



// indicates the chromaticity coordinates of the source color primaries

colorPrimaries:           number, // enumeration [0-255]



// opto-electronic transfer characteristic function (ex. PQ, HLG)

transferCharacteristics:  number, // enumeration [0-255]



// matrix coefficients used in deriving luma and chroma signals

matrixCoefficients:       number, // enumeration [0-255]

},



hdrCll: {

//

// maximum value of the frame average light level

// (in 1 cd/m2) of the entire playback sequence

//

maxFall:  number,     // [0.0001-10000]



//

// maximum light level of any single pixel (in 1 cd/m2)

// of the entire playback sequence

//

maxCLL:   number,     // [0.0001-10000]

},



//

// The hdrMdcv object defines mastering display (i.e., where

// creative work is done during the mastering process) color volume (a.k.a., mdcv)

// metadata which describes primaries, white point and min/max luminance. The

// hdrMdcv object SHOULD be provided.

//

// Specification of the metadata along with its ranges adhere to the

// ST 2086:2018 - SMPTE Standard (except for minLuminance see

// comments below)

//

hdrMdcv: {

//

// Mastering display color volume (mdcv) xy Chromaticity Coordinates within CIE

// 1931 color space.

//

// Values SHALL be specified with four decimal places. The x coordinate SHALL

// be in the range [0.0001, 0.7400]. The y coordinate SHALL be

// in the range [0.0001, 0.8400].

//

redX:         number,

redY:         number,

greenX:       number,

greenY:       number,

blueX:        number,

blueY:        number,

whitePointX:  number,

whitePointY:  number,



//

// max/min display luminance of the mastering display (in 1 cd/m2 ie. nits)

//

// note: ST 2086:2018 - SMPTE Standard specifies minimum display mastering

// luminance in multiples of 0.0001 cd/m2.

//

// For consistency we specify all values

// in 1 cd/m2. Given that a hypothetical perfect screen has a peak brightness

// of 10,000 nits and a black level of .0005 nits we do not need to

// switch units to 0.0001 cd/m2 to increase resolution on the lower end of the

// minLuminance property. The ranges (in nits) mentioned below suffice

// the theoretical limit for Mastering Reference Displays and adhere to the

// SMPTE ST 2084 standard (a.k.a., PQ) which is capable of representing full gamut

// of luminance level.

//

maxLuminance: number,     // [5-10000]

minLuminance: number,     // [0.0001-5]

},

}

flv mux

在libavformat/flvenc.c中,新增flv_write_metadata_packet函数,用于添加metadata。

flv的数据结构:

typedef struct FLVContext {

.....

int metadata_pkt_written;//0: 还未写过,需要写;1: 写过了,不需要写。

} FLVContext;

在函数flv_write_metadata_packet中实现,见注释:


static void flv_write_metadata_packet(AVFormatContext *s, AVCodecParameters *par, unsigned int ts) {

FLVContext *flv = s->priv_data;



//不需要写入,就返回;可以通过设置这个标志变量来使能/去使能更新写metadata

if (flv->metadata_pkt_written)

return;

//支持h265, av1, vp9

if (par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) {

......

//写入tag头,标识其为视频

avio_w8(pb, FLV_TAG_TYPE_VIDEO); //write video tag type

metadata_size_pos = avio_tell(pb);

avio_wb24(pb, 0 + flags_size);

put_timestamp(pb, ts); //ts = pkt->dts, gen

avio_wb24(pb, flv->reserved);



//根据enhance rtmp标志,写入FLV_IS_EX_HEADER标识,和fourCC的字段(hvc1 or av01 or vp09)

if (par->codec_id == AV_CODEC_ID_HEVC) {

avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeMetadata| FLV_FRAME_VIDEO_INFO_CMD); // ExVideoTagHeader mode with PacketTypeMetadata

avio_write(pb, "hvc1", 4);

} else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) {

avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeMetadata| FLV_FRAME_VIDEO_INFO_CMD);

avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4);

}

//下面为写入AMF格式的hdr相关的colorInfo数据

avio_w8(pb, AMF_DATA_TYPE_STRING);

put_amf_string(pb, "colorInfo");



avio_w8(pb, AMF_DATA_TYPE_OBJECT);



put_amf_string(pb, "colorConfig");  // colorConfig



avio_w8(pb, AMF_DATA_TYPE_OBJECT);



if (par->color_trc != AVCOL_TRC_UNSPECIFIED &&

par->color_trc < AVCOL_TRC_NB) {

put_amf_string(pb, "transferCharacteristics");  // color_trc

put_amf_double(pb, par->color_trc);

}



if (par->color_space != AVCOL_SPC_UNSPECIFIED &&

par->color_space < AVCOL_SPC_NB) {

put_amf_string(pb, "matrixCoefficients"); // colorspace

put_amf_double(pb, par->color_space);

}



if (par->color_primaries != AVCOL_PRI_UNSPECIFIED &&

par->color_primaries < AVCOL_PRI_NB) {

put_amf_string(pb, "colorPrimaries"); // color_primaries

put_amf_double(pb, par->color_primaries);

}



put_amf_string(pb, "");

avio_w8(pb, AMF_END_OF_OBJECT);



if (lightMetadata) {

put_amf_string(pb, "hdrCll");

avio_w8(pb, AMF_DATA_TYPE_OBJECT);



put_amf_string(pb, "maxFall");

put_amf_double(pb, lightMetadata->MaxFALL);



put_amf_string(pb, "maxCLL");

put_amf_double(pb, lightMetadata->MaxCLL);



put_amf_string(pb, "");

avio_w8(pb, AMF_END_OF_OBJECT);

}



if (displayMetadata && (displayMetadata->has_primaries || displayMetadata->has_luminance)) {

put_amf_string(pb, "hdrMdcv");

avio_w8(pb, AMF_DATA_TYPE_OBJECT);

if (displayMetadata->has_primaries) {

put_amf_string(pb, "redX");

put_amf_double(pb, av_q2d(displayMetadata->display_primaries[0][0]));



put_amf_string(pb, "redY");

put_amf_double(pb, av_q2d(displayMetadata->display_primaries[0][1]));



put_amf_string(pb, "greenX");

put_amf_double(pb, av_q2d(displayMetadata->display_primaries[1][0]));



put_amf_string(pb, "greenY");

put_amf_double(pb, av_q2d(displayMetadata->display_primaries[1][1]));



put_amf_string(pb, "blueX");

put_amf_double(pb, av_q2d(displayMetadata->display_primaries[2][0]));



put_amf_string(pb, "blueY");

put_amf_double(pb, av_q2d(displayMetadata->display_primaries[2][1]));



put_amf_string(pb, "whitePointX");

put_amf_double(pb, av_q2d(displayMetadata->white_point[0]));



put_amf_string(pb, "whitePointY");

put_amf_double(pb, av_q2d(displayMetadata->white_point[1]));

}

if (displayMetadata->has_luminance) {

put_amf_string(pb, "maxLuminance");

put_amf_double(pb, av_q2d(displayMetadata->max_luminance));



put_amf_string(pb, "minLuminance");

put_amf_double(pb, av_q2d(displayMetadata->min_luminance));

}

put_amf_string(pb, "");

avio_w8(pb, AMF_END_OF_OBJECT);

}

put_amf_string(pb, "");

avio_w8(pb, AMF_END_OF_OBJECT);



flv->metadata_pkt_written = 1;//标识写过了

}

}

其中HDR的数据来源为AVCodecParameters *par数据结构中的内容:

typedef struct AVCodecParameters {

....

/**

* Video only. Additional colorspace characteristics.

*/

enum AVColorRange                  color_range;

enum AVColorPrimaries              color_primaries;

enum AVColorTransferCharacteristic color_trc;

enum AVColorSpace                  color_space;

enum AVChromaLocation              chroma_location;



/*

* 类型: AV_PKT_DATA_CONTENT_LIGHT_LEVEL, 数据: AVContentLightMetadata* lightMetadata

* 类型: AV_PKT_DATA_MASTERING_DISPLAY_METADATA, 数据: AVMasteringDisplayMetadata *displayMetadata

*/

AVPacketSideData *coded_side_data;

....

}

flv demux

解析函数在libavformat/flvdec.c文件中,函数amf_parse_object中。

static int amf_parse_object(AVFormatContext *s, AVStream *astream,

AVStream *vstream, const char *key,

int64_t max_pos, int depth)
{

FLVMetaVideoColor *meta_video_color = flv->metaVideoColor;

......

if (meta_video_color) {

if (amf_type == AMF_DATA_TYPE_NUMBER ||

amf_type == AMF_DATA_TYPE_BOOL) {

if (!strcmp(key, "colorPrimaries")) {

meta_video_color->primaries = num_val;

} else if (!strcmp(key, "transferCharacteristics")) {

meta_video_color->transfer_characteristics = num_val;

} else if (!strcmp(key, "matrixCoefficients")) {

meta_video_color->matrix_coefficients = num_val;

} else if (!strcmp(key, "maxFall")) {

meta_video_color->max_fall = num_val;

} else if (!strcmp(key, "maxCLL")) {

meta_video_color->max_cll = num_val;

} else if (!strcmp(key, "redX")) {

meta_video_color->mastering_meta.r_x = num_val;

} else if (!strcmp(key, "redY")) {

meta_video_color->mastering_meta.r_y = num_val;

} else if (!strcmp(key, "greenX")) {

meta_video_color->mastering_meta.g_x = num_val;

} else if (!strcmp(key, "greenY")) {

meta_video_color->mastering_meta.g_y = num_val;

} else if (!strcmp(key, "blueX")) {

meta_video_color->mastering_meta.b_x = num_val;

} else if (!strcmp(key, "blueY")) {

meta_video_color->mastering_meta.b_y = num_val;

} else if (!strcmp(key, "whitePointX")) {

meta_video_color->mastering_meta.white_x = num_val;

} else if (!strcmp(key, "whitePointY")) {

meta_video_color->mastering_meta.white_y = num_val;

} else if (!strcmp(key, "maxLuminance")) {

meta_video_color->mastering_meta.max_luminance = num_val;

} else if (!strcmp(key, "minLuminance")) {

meta_video_color->mastering_meta.min_luminance = num_val;

}

}

}

......

}

相关推荐

Github霸榜的SpringBoot全套学习教程,从入门到实战,内容超详细

前言...

SpringBoot+LayUI后台管理系统开发脚手架

源码获取方式:关注,转发之后私信回复【源码】即可免费获取到!项目简介本项目本着避免重复造轮子的原则,建立一套快速开发JavaWEB项目(springboot-mini),能满足大部分后台管理系统基础开...

Spring Boot+Vue全栈开发实战,中文版高清PDF资源

SpringBoot+Vue全栈开发实战,中文高清PDF资源,需要的可以私我:)SpringBoot致力于简化开发配置并为企业级开发提供一系列非业务性功能,而Vue则采用数据驱动视图的方式将程序...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础...

探秘Spring Cache:让Java应用飞起来的秘密武器

探秘SpringCache:让Java应用飞起来的秘密武器在当今快节奏的软件开发环境中,性能优化显得尤为重要。SpringCache作为Spring框架的一部分,为我们提供了强大的缓存管理能力,让...

3,从零开始搭建SSHM开发框架(集成Spring MVC)

目录本专题博客已共享在(这个可能会更新的稍微一些)https://code.csdn.net/yangwei19680827/maven_sshm_blog...

Spring Boot中如何使用缓存?超简单

SpringBoot中的缓存可以减少从数据库重复获取数据或执行昂贵计算的需要,从而显著提高应用程序的性能。SpringBoot提供了与各种缓存提供程序的集成,您可以在应用程序中轻松配置和使用缓...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

1,从零开始搭建SSHM开发框架(环境准备)

目录本专题博客已共享在https://code.csdn.net/yangwei19680827/maven_sshm_blog1,从零开始搭建SSHM开发框架(环境准备)...

做一个适合二次开发的低代码平台,把程序员从curd中解脱出来-1

干程序员也有好长时间了,大多数时间都是在做curd。现在想做一个通用的curd平台直接将我们解放出来;把核心放在业务处理中。用过代码生成器,在数据表设计好之后使用它就可以生成需要的controller...

设计一个高性能Java Web框架(java做网站的框架)

设计一个高性能JavaWeb框架在当今互联网高速发展的时代,构建高性能的JavaWeb框架对于提升用户体验至关重要。本文将从多个角度探讨如何设计这样一个框架,让我们一起进入这段充满挑战和乐趣的旅程...

【推荐】强&amp;牛!一款开源免费的功能强大的代码生成器系统!

今天,给大家推荐一个代码生成器系统项目,这个项目目前收获了5.3KStar,个人觉得不错,值得拿出来和大家分享下。这是我目前见过最好的代码生成器系统项目。功能完整,代码结构清晰。...

Java面试题及答案总结(2025版持续更新)

大家好,我是Java面试分享最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试场景题及答案。...

Java开发网站架构演变过程-从单体应用到微服务架构详解

Java开发网站架构演变过程,到目前为止,大致分为5个阶段,分别为单体架构、集群架构、分布式架构、SOA架构和微服务架构。下面玄武老师来给大家详细介绍下这5种架构模式的发展背景、各自优缺点以及涉及到的...

本地缓存GuavaCache(一)(guava本地缓存原理)

在并发量、吞吐量越来越大的情况下往往是离不开缓存的,使用缓存能减轻数据库的压力,临时存储数据。根据不同的场景选择不同的缓存,分布式缓存有Redis,Memcached、Tair、EVCache、Aer...