[C/C++] FFmpeg提取HDR视频封面图

897 阅读2分钟

用FFmpeg提取视频封面图这是一个很常见的解决方案,但是我发现几乎没什么文章在里面涉及到HDR视频。

我们先看原视频:

471691073822_.pic_hd.jpg 不处理HDR视频时提取的封面图: thumbnail.jpg 处理HDR视频后提取的封面图: thumbnail.jpg 可以明显看出来,不处理HDR视频时明显色彩泛白,丢失颜色。处理HDR视频后颜色饱满,但是目前出现了色彩稍微过曝的问题,这个其实只需要调整滤镜参数,我也没仔细研究过色彩空间,目前是复制的网上的滤镜参数,有更高要求的请自行调整滤镜参数。

首先,判断视频是否是HDR视频。

判断原理,如果满足下面的2个条件就是HDR视频(来源:video.stackexchange.com/a/33827/420…):

  1. ColorPrimarie是否是BT2020
  2. ColorTransfer是否是SMPTE2084或ARIB_STD_B67
  • 脚本方式同样可以参考这个链接的采纳答案:
COLORS=$(ffprobe -show_streams -v error "${F}" |egrep "^color_transfer|^color_space=|^color_primaries=" |head -3)
for C in $COLORS; do
        if [[ "$C" = "color_space="* ]]; then
                COLORSPACE=${C##*=}
        elif [[ "$C" = "color_transfer="* ]]; then
                COLORTRANSFER=${C##*=}
        elif [[ "$C" = "color_primaries="* ]]; then
                COLORPRIMARIES=${C##*=}
        fi      
done    
if [ "${COLORSPACE}" = "bt2020nc" ] && [ "${COLORTRANSFER}" = "smpte2084" ] && [ "${COLORPRIMARIES}" = "bt2020" ]; then 
        echo ${F}
fi
  • 代码实现方式:
AVCodecParameters *codecParams = format_ctx->streams[video_stream_index]->codecpar;
auto colorTransfer = (AVColorTransferCharacteristic) codecParams->color_trc;
auto colorPrimaries = (AVColorPrimaries) codecParams->color_primaries;

if (colorPrimaries == AVCOL_PRI_BT2020 && (colorTransfer == AVCOL_TRC_SMPTE2084 || colorTransfer == AVCOL_TRC_ARIB_STD_B67)) {
    // HDR视频
    printf("HDR video\n");
}

然后,使用滤镜

  • 使用命令方式:
ffmpeg -i HDR.mov -vf zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p '%05d.png'

命令来源: superuser.com/a/1732684/1…

  • 代码实现方式:
std::string filter_descr=zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p;

initFilter(filter_descr.c_str(),...);
//省略,因为有点多,在项目里懒得分出来。实在有需要的再说。。。

不过可以参考官方的链接,因为我也是参照官方的链接: github.com/FFmpeg/FFmp…

最后,如果遇到提取的封面图旋转了90°,同样可以使用滤镜

因为这个使用命令的时候会自动处理旋转问题,所以仅仅只有代码方式需要自行处理。

还是看看这个角度信息吧:

iShot_2023-08-03_23.09.04.png 判断与获取旋转角度:

double get_rotation(AVStream *st) {
    AVDictionaryEntry *rotate_tag = av_dict_get(st->metadata, "rotate", nullptr, 0);
    uint8_t *displayMatrix = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, nullptr);
    double theta = 0;
    if (rotate_tag && (*rotate_tag->value) && strcmp(rotate_tag->value, "0") != 0) {
        char *tail;
        theta = av_strtod(rotate_tag->value, &tail);
        if (*tail) {
            theta = 0;
        }
    }
    if (displayMatrix && theta == 0) {
        theta = -av_display_rotation_get((int32_t *) displayMatrix);
    }
    theta -= 360 * floor(theta / 360 + 0.9 / 360);
    return theta;
}

如果角度不是0就需要旋转,比如视频是逆时针旋转90度的,那我们顺时针旋转90度就好了:

std::string filter_descr="transpose=clock";

完。