.h264 .h265 压缩率的直观感受

news2024/11/26 15:28:57

1.资源文件  

https://download.csdn.net/download/twicave/89579327


上面是.264 .265和原始的YUV420文件,各自的大小。

2.转换工具:

2.1 .h264 .h265互转

可以使用ffmpeg工具:Builds - CODEX FFMPEG @ gyan.dev

命令行参数:

ffmpeg -i Tennis1080p.h264 -c:v libx265 -preset medium -crf 28 Tennis1080p.h265 

2.2 .h264 转 yuv

我因为要测试rk3588上一个硬件解码器,使用的是一个.c代码:

mpp-test: Rockchip MPP的简单案例

2.3 yuv file reader

 这个可以使用python直接处理:

import cv2
import numpy as np


yuv_frame_pack_file = "Tennis1080p.yuv"

def yuv420sp_to_rgb(nv12, width, height):
        print('lenOfframe = ', len(nv12))
        nv12_array = np.frombuffer(nv12, dtype=np.uint8)
        #if len(nv12_array) > (width * height * 3 // 2):
        #    height += ((len(nv12_array) - (width * height * 3 // 2)) // width * 2) // 3
        y_plane_size = height * width
        uv_plane_size = (height // 2) * (width // 2) * 2
        # Split the NV12 data into Y plane and UV plane
        y_plane = nv12_array[0:y_plane_size].reshape(height, width)
        
        uv_plane = nv12_array[y_plane_size:y_plane_size+uv_plane_size].reshape(height // 2, width)  # UV plane is half the height of Y plane
    
        # Create a new NumPy array for the YUV image, with the same data as Y plane
        # but with a shape that OpenCV expects for YUV420sp (NV12)
        yuv420sp = np.zeros((height + height // 2, width), dtype=np.uint8)
        yuv420sp[:height, :] = y_plane  # Copy Y plane data
        yuv420sp[height:, ::2] = uv_plane[:, 1::2]  # Copy U plane data
        yuv420sp[height:, 1::2] = uv_plane[:, ::2]  # Copy V plane data

        '''
        # 文件路径
        file_path = 'yuv420sp.bin'
        # 打开文件并写入数据
        with open(file_path, 'wb') as file:
            file.write(nv12)
        sys.exit(0)
        '''
    
        # Use OpenCV to convert YUV420sp (NV12) to RGB
        rgb_image = cv2.cvtColor(yuv420sp, cv2.COLOR_YUV2RGB_NV12)
    
        return rgb_image


# 设置图像的宽度和高度
width, height = 1920, 1080

# 读取一帧YUV420数据
cnt = 23
with open(yuv_frame_pack_file , 'rb') as file:
    while(cnt>0):
        cnt -=1
        yuv420_frame = file.read(width * height * 3 // 2)  # YUV420格式,每帧大小为width * height * 3 / 2
        continue
    yuv420_frame = file.read(width * height * 3 // 2)  # YUV420格式,每帧大小为width * height * 3 / 2
    yuv420_frame = np.frombuffer(yuv420_frame, dtype=np.uint8)
    rgb_frame = yuv420sp_to_rgb(yuv420_frame, width, height)
    cv2.imshow('RGB Image', rgb_frame)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

2.3.1解码效果

附录A h.265=>yuv frames file转换工具 

实际测试过程中,您可能需要对.h264, .h265的转换速度做比对,这里给出.h265转储为yuv frames file的C代码,相较2.2的原始c代码修改很少:

我只改了mpp_init,和需要处理的.h265文件名。

/**
 * 1. make
 * 2. ./mpp-dec-h264-to-yuv-file
 * 3. gst-launch-1.0 filesrc location=Tennis1080p.yuv ! videoparse width=1920 height=1080 format=nv12 ! videoconvert ! xvimagesink
 * 4. gst-launch-1.0 filesrc location=Tennis1080p.h264 ! h264parse ! mppvideodec ! xvimagesink
 */
#include <unistd.h>
#include <stdio.h>
#include <rockchip/rk_mpi.h>

#define __IN_FILE__ ("Tennis1080p.h265")
#define __OUT_FILE__ ("Tennis1080p.yuv")

void dump_frame(MppFrame frame, FILE *out_fp)
{
    printf("dump_frame_to_file\n");

    RK_U32 width    = 0;
    RK_U32 height   = 0;
    RK_U32 h_stride = 0;
    RK_U32 v_stride = 0;
    MppFrameFormat fmt  = MPP_FMT_YUV420SP;
    MppBuffer buffer    = NULL;
    RK_U8 *base = NULL;

    width    = mpp_frame_get_width(frame);
    height   = mpp_frame_get_height(frame);
    h_stride = mpp_frame_get_hor_stride(frame);
    v_stride = mpp_frame_get_ver_stride(frame);
    fmt      = mpp_frame_get_fmt(frame);
    buffer   = mpp_frame_get_buffer(frame);

    RK_U32 buf_size = mpp_frame_get_buf_size(frame);
    printf("w x h: %dx%d hor_stride:%d ver_stride:%d buf_size:%d\n",
           width, height, h_stride, v_stride, buf_size);
           
    if (NULL == buffer) {
        printf("buffer is null\n");
        return ;
    }

    base = (RK_U8 *)mpp_buffer_get_ptr(buffer);

    // MPP_FMT_YUV420SP
    if (fmt != MPP_FMT_YUV420SP) {
        printf("fmt %d not supported\n", fmt);
        return;
    }

    RK_U32 i;
    RK_U8 *base_y = base;
    RK_U8 *base_c = base + h_stride * v_stride;

    for (i = 0; i < height; i++, base_y += h_stride) {
        fwrite(base_y, 1, width, out_fp);
    }
    for (i = 0; i < height / 2; i++, base_c += h_stride) {
        fwrite(base_c, 1, width, out_fp);
    }
}

void dump_frame_to_file(MppCtx ctx, MppApi *mpi, MppFrame frame, FILE *out_fp)
{
    printf("decode_and_dump_to_file\n");

    MPP_RET ret;

    if (mpp_frame_get_info_change(frame)) {
        printf("mpp_frame_get_info_change\n");
        /**
         * 第一次解码会到这个分支,需要为解码器设置缓冲区.
         * 解码器缓冲区支持3种模式。参考【图像内存分配以及交互模式】Rockchip_Developer_Guide_MPP_CN.pdf
         * 这里使用纯内部模式。
         */
        ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
        if (ret) {
            printf("mpp_frame_get_info_change mpi->control error"
                    "MPP_DEC_SET_INFO_CHANGE_READY %d\n", ret);
        }
        return;
    }

    RK_U32 err_info = mpp_frame_get_errinfo(frame);
    RK_U32 discard = mpp_frame_get_discard(frame);    
    printf("err_info: %u discard: %u\n", err_info, discard);

    if (err_info) {
        return;
    }
        
    // save
    dump_frame(frame, out_fp);
    return;
}

int main(void)
{
    printf("---------- mpp start ----------\n");

    // 1. 打开输入文件
    FILE *in_fp = fopen(__IN_FILE__, "rb");
    if (!in_fp) {
        printf("fopen error\n");
        return -1;
    }

    // 2. 打开输出文件
    FILE *out_fp = fopen(__OUT_FILE__, "wb+");
    if (!out_fp) {
        printf("fopen error\n");
        return -1;
    }

    // 3. 初始化解码器上下文,MppCtx MppApi
    MppCtx ctx = NULL;
    MppApi *mpi = NULL;
    MPP_RET ret = mpp_create(&ctx, &mpi);
    if (MPP_OK != ret) {
        printf("mpp_create error\n");
        return -1;
    }

    /**
     * 4. 配置解器
     *      - 解码文件需要 split 模式
     *      - 设置非阻塞模式,0非阻塞(默认),-1阻塞,+val 超时(ms)
     */
    RK_U32 need_split = -1;
    ret = mpi->control(ctx, MPP_DEC_SET_PARSER_SPLIT_MODE, (MppParam*)&need_split);
    if (MPP_OK != ret) {
        printf("mpi->control error MPP_DEC_SET_PARSER_SPLIT_MODE\n");
        return -1;
    }
    
    ret = mpp_init(ctx, MPP_CTX_DEC, MPP_VIDEO_CodingHEVC);  // 固定为H265 https://blog.csdn.net/weixin_38807927/article/details/135760601
    if (MPP_OK != ret) {
        printf("mpp_init error\n");
        return -1;
    }

    // 5. 初始化包,MppPacket
    int buf_size = 5 * 1024 * 1024;
    char *buf = (char*)malloc(buf_size);
    if (!buf) {
        printf("malloc error\n");
        return -1;
    }
    MppPacket pkt = NULL;
    ret = mpp_packet_init(&pkt, buf, buf_size);
    if (MPP_OK != ret) {
        printf("mpp_packet_init error\n");
        return -1;
    }

    // 6. 循环读取文件,输入解码器,解码,保存结果
    int over = 0;
    while (!over) {
        printf("decode...\n");
        int len = fread(buf, 1, buf_size, in_fp);
        printf("read file length:%d\n", len);

        if (0 < len) {
            mpp_packet_write(pkt, 0, buf, len);
            mpp_packet_set_pos(pkt, buf);
            mpp_packet_set_length(pkt, len);
            if (feof(in_fp) || len < buf_size) {  // 文件读完,设置结束标志位
                mpp_packet_set_eos(pkt);
                printf("mpp_packet_set_eos\n");
            }
        }

        /**
         * decode_put_packet返回失败,意味着内部缓冲区已满。
         * 非阻塞模式,使用pkt_is_send判断当前读取的数据包(buf)是否成功发送。
         */
        int pkt_is_send = 0;
        while (!pkt_is_send && !over) {
            if (0 < len) {
                printf("pkt remain:%d\n", mpp_packet_get_length(pkt));
                ret = mpi->decode_put_packet(ctx, pkt);
                if (MPP_OK == ret) {
                    printf("pkt send success remain:%d\n", mpp_packet_get_length(pkt));
                    pkt_is_send = 1;
                }
            }

            MppFrame frame;
            MPP_RET ret;
            ret = mpi->decode_get_frame(ctx, &frame);
            if (MPP_OK != ret || !frame) {
                printf("decode_get_frame falied ret:%d\n", ret);
                usleep(2000);  // 等待一下2ms,通常1080p解码时间2ms
                continue;
            }

            printf("decode_get_frame success\n");
            dump_frame_to_file(ctx, mpi, frame, out_fp);

            if (mpp_frame_get_eos(frame)) {
                printf("mpp_frame_get_eos\n");
                mpp_frame_deinit(&frame);
                over = 1;
                continue;
            }
            mpp_frame_deinit(&frame);
        }
    }

    // 7. 释放资源
    fclose(in_fp);
    fclose(out_fp);
    mpi->reset(ctx);
    mpp_packet_deinit(&pkt);
    mpp_destroy(ctx);
    free(buf);
    
    printf("---------- mpp over ----------\n");
    return 0;
}

A.1相应的make file :

将文件放置在.c文件的同级目录,命名为:makefile

app: mpp-dec-h264-to-yuv-file.c mpp-multi-thread-demo.c mpp-dec-h265-to-yuv-file.c
	gcc mpp-dec-h264-to-yuv-file.c -o mpp-dec-h264-to-yuv-file -lrockchip_mpp
	gcc mpp-dec-h265-to-yuv-file.c -o mpp-dec-h265-to-yuv-file -lrockchip_mpp
	gcc mpp-multi-thread-demo.c -o mpp-multi-thread-demo -lrockchip_mpp -lpthread -I/usr/include/glib-2.0 -I/usr/lib/aarch64-linux-gnu/glib-2.0/include -lglib-2.0

需要编译时:执行

make

即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1952479.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树详解-第四篇 二叉树链式结构的实现

目录 1.二叉树的遍历 1.1前序遍历&#xff1a; 1.2 中序遍历&#xff1a; 1.3 后序遍历&#xff1a; 2.二叉树链式结构的实现 2.1 Tree.h 2.2 Tree.cpp 2.2.1 前序遍历 void PreOrder(TNode* Root) 2.2.2 中序遍历 void InOrder(TNode* Root) 2.2.3 后序遍历 void Bac…

Linux中断框架

不管是裸机实验还是 Linux 下的驱动实验&#xff0c;中断都是频繁使用的功能&#xff0c;在裸机中使用中断我们需要做一大堆的工作&#xff0c;比如配置寄存器&#xff0c;使能 IRQ 等等。Linux 内核提供了完善的中断框架&#xff0c;我们只需要申请中断&#xff0c;然后注册中…

【rockyLinux】rockyLinux 9.4 安装 java jdk

一、安装 java 1.选择版本 yum list | grep jdk各个版本之间的区别&#xff1a; 2.选择了&#xff1a;java-17-openjdk-devel.x86_64&#xff08;开发者版本&#xff09;&#xff0c;开始安装 建议安装在 /usr/local 这个目录下&#xff0c;可以创建一个 app 目录来收录它…

重生奇迹MU 三个阶段三种体验

重生奇迹MU的玩家们通常按等级分为三个阶段。在这三个不同的阶段里&#xff0c;游戏中的玩家好像深入了三个不同的世界&#xff0c;经历了三种不同的游戏体验&#xff0c;收获了三种不同的精彩&#xff01; 从刚开始游戏一直到达到400级&#xff0c;是游戏中的第一个阶段&…

0719_驱动3 printk使用方法

一、printk使用方法 1.应用层打印使用printf&#xff0c;内核层使用printk 2.如何查看内核层中printk如何使用 3.在内核空间执行grep "printk" * -nR 4.在内核空间执行vi -t KERN_INFO 5.printk有8中打印级别&#xff08;0-7&#xff09;&#xff0c;打印级别用来过滤…

暴雨宅家?AI拯救你的无聊暑假!高中生必藏神器大公开

嘿&#xff0c;各位高中生朋友们&#xff0c;最近是不是被这没完没了的暴雨困在家里&#xff0c;感觉暑假生活都快发霉了&#xff1f;别急&#xff0c;今天我要揭秘一个宝藏网站—— ai123.cn&#xff0c;它简直就是咱们暑期宅家必备的救星&#xff01;接下来&#xff0c;我就来…

Selenium相对定位

测试网站&#xff1a;Web form 相对定位的方法&#xff1a; above()&#xff1a;定位基准元素上方的元素below()&#xff1a;定位基准元素下方的元素to_left_of()&#xff1a;定位基准元素左侧的元素to_right_of()&#xff1a;定位基准元素右侧的元素near() &#xff1a;定位基…

Windows横向渗透

准备靶机windows&#xff1a;192.168.10.131 准备攻击机&#xff1a;192.168.10.130 1.用cs连接到了windows靶机 2.使用cs探测内网信息&#xff1a; 查看当前目标系统网络情况&#xff0c;确认目标系统所在内网网段为10段 3.使用cs的portscan功能对内网进行扫描 portscan 1…

aarch64环境安装minio

1.去官网查看最新版本 MinIO | 用于AI的S3 & Kubernetes原生对象存储 2.使用命令安装,采取wget和dnf安装 wget https://dl.min.io/server/minio/release/linux-arm64/archive/minio-20240113075303.0.0-1.aarch64.rpm -O minio.rpm sudo dnf install minio.rpm 注意点…

MATLAB学习日志DAY16

16.数组类型&#xff08;1&#xff09; 16.1多维数组 MATLAB 环境中的多维数组是具有多个下标的数组。创建多维数组的一种方法是调用具有多个参数的 zeros、ones、rand 或 randn。 R randn(3,4,5); 创建一个 345 数组&#xff0c;共包含 3*4*5 60 个正态分布的随机元素。…

springboot在加了mapper之后报错

springboot在加了mapper之后报错 最后发现是spring boot版本不兼容&#xff0c;spring-boot-starter-parent换成3.0.5之后可以了

大模型金九银十秋招:AI时代的就业新趋势,收藏我这篇就够了非常详细

随着人工智能技术的飞速发展&#xff0c;大模型&#xff08;Large Models&#xff09;在各个领域的应用日益广泛&#xff0c;从自然语言处理到图像识别&#xff0c;从自动驾驶到智能推荐系统&#xff0c;大模型正逐渐成为AI领域的新宠。在这个背景下&#xff0c;大模型的秋招&a…

ubuntu串口重命名助手x86_64电脑架构(秒速配好串口)(上)软件介绍

✨✨ Rqtz 个人主页 : 点击✨✨ &#x1f388;PyQt系列专栏:点击&#x1f388; &#x1f388;Qt智能车上位机专栏: 点击&#x1f388; &#x1f388;Qt串口助手专栏:点击&#x1f388; &#x1f4ab;宗旨:共享IT之美,共创机器未来 目录 ​编辑 ​编辑 项目背景 相关参数…

基于Pytorch框架的深度学习densenet121神经网络鸟类行为识别分类系统源码

第一步&#xff1a;准备数据 5种鸟类行为数据&#xff1a;self.class_indict ["bowing_status", "grooming", "headdown", "vigilance_status", "walking"] &#xff0c;总共有23790张图片&#xff0c;每个文件夹单独放一…

从零开始学习网络安全渗透测试之基础入门篇——(三)APP架构小程序H5+Vue语言Web封装原生开发Flutter

从零开始学习网络安全渗透测试之基础入门篇——&#xff08;三&#xff09; #APP架构&小程序&H5Vue语言&Web封装&原生开发&Flutter 确保App和小程序的安全性是移动应用开发中的重要环节。比如&#xff0c;代码混淆、数据加密、安全审计、权限管理、API安…

[240727] Qt Creator 14 发布 | AMD 推迟 Ryzen 9000芯片发布

目录 Qt Creator 14 发布Qt Creator 14 版本发布&#xff0c;带来一系列新功能和改进终端用户可通过命令行方式查看此新闻终端用户可通过命令行方式安装软件&#xff1a; AMD 推迟 Ryzen 9000芯片发布 Qt Creator 14 发布 Qt Creator 14 版本发布&#xff0c;带来一系列新功能…

IntelliJ IDEA 中安装 Groovy 插件 添加 Groovy SDK

一、在 IntelliJ IDEA 中安装 Groovy 插件可以按照以下步骤进行&#xff1a; 打开 IntelliJ IDEA&#xff1a; 启动 IntelliJ IDEA。 打开插件市场&#xff1a; 进入 File -> Settings&#xff08;在 macOS 上是 IntelliJ IDEA -> Preferences&#xff09;。在左侧菜单中…

信号、电源、网线、传感器防雷,SPD系列防雷器全覆盖!

信号、电源、网线、传感器防雷&#xff0c;SPD系列防雷器全覆盖&#xff01; SPD 系列防雷器是一种安装于被保护设备一侧的产品&#xff0c;旨在保护电子设备免受雷击、电磁脉冲、过压过渡冲击等造成的设备损坏。该产品广泛应用于工业控制、安全监测、网络通讯、交通电压等行业…

高速板开源工程的学习(一)

泰山派NAS-原理图和PCB设计经验分享-塞塞哇 (saisaiwa.com) BGA扇出的时候千万小心&#xff0c;导线到焊盘的距离大于0.1MM,千万小心&#xff0c;不然会寄寄的&#xff0c;这个在设计规则里面可以设置&#xff1a; 这种就容易造成阻焊开窗的误判&#xff0c;是很不规范的&…