Charley王大王

Charley王大王

0个粉丝

3

问答

0

专栏

0

资料

Charley王大王  发布于  2026-01-19 16:58:01
采纳率 0%
3个问答
61

自己手动创建venc通道后,通过ss_mpi_vb_get_blk获取vb将yuv填充之后进行编码,请问什么时候进行vb blk的释放?

   
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include "sample_comm.h"
#include "ot_common.h"
#include "ot_common_video.h"
#include "ot_common_venc.h"
#include "ss_mpi_venc.h"
#include "ss_mpi_vb.h"
#include "ss_mpi_sys.h"  // 添加系统映射相关头文件

static td_bool g_sample_venc_exit = TD_FALSE;

/* 信号处理 */
static td_void sample_venc_handle_sig(td_s32 signo)
{
    if (signo == SIGINT || signo == SIGTERM) {
        g_sample_venc_exit = TD_TRUE;
    }
}

/* 创建VENC通道 */
static td_s32 sample_venc_create_chn_direct(ot_venc_chn venc_chn, ot_size *size, 
                                           ot_payload_type type)
{
    td_s32 ret;
    ot_venc_chn_attr chn_attr = {0};

    // 1. 配置编码属性
    chn_attr.venc_attr.type = type;
    chn_attr.venc_attr.max_pic_width = size->width;
    chn_attr.venc_attr.max_pic_height = size->height;
    chn_attr.venc_attr.buf_size = size->width * size->height * 3 / 2;
    chn_attr.venc_attr.pic_width = size->width;
    chn_attr.venc_attr.pic_height = size->height;
    chn_attr.venc_attr.is_by_frame = TD_TRUE;

    if (type == OT_PT_H264) {
        chn_attr.venc_attr.profile = 0;  // Baseline Profile
        // H.264 特定属性
        chn_attr.venc_attr.h264_attr.rcn_ref_share_buf_en = TD_FALSE;
        chn_attr.venc_attr.h264_attr.frame_buf_ratio = 70;
    } else if (type == OT_PT_H265) {
        chn_attr.venc_attr.profile = 0;  // Main Profile
        // H.265 特定属性
        chn_attr.venc_attr.h265_attr.rcn_ref_share_buf_en = TD_FALSE;
        chn_attr.venc_attr.h265_attr.frame_buf_ratio = 70;
    }

    // 2. 配置码率控制(CBR模式)
    chn_attr.rc_attr.rc_mode = OT_VENC_RC_MODE_H264_CBR;  // 修正:使用通用的CBR模式

    // 配置CBR参数
    if (type == OT_PT_H264) {
        chn_attr.rc_attr.h264_cbr.gop = 30;
        chn_attr.rc_attr.h264_cbr.stats_time = 1;
        chn_attr.rc_attr.h264_cbr.src_frame_rate = 30;
        chn_attr.rc_attr.h264_cbr.dst_frame_rate = 30;
        chn_attr.rc_attr.h264_cbr.bit_rate = 2000;  // 2Mbps
    } else if (type == OT_PT_H265) {
        chn_attr.rc_attr.h265_cbr.gop = 30;
        chn_attr.rc_attr.h265_cbr.stats_time = 1;
        chn_attr.rc_attr.h265_cbr.src_frame_rate = 30;
        chn_attr.rc_attr.h265_cbr.dst_frame_rate = 30;
        chn_attr.rc_attr.h265_cbr.bit_rate = 2000;  // 2Mbps
    }

    // 3. 配置GOP属性(使用正常P帧模式)
    chn_attr.gop_attr.gop_mode = OT_VENC_GOP_MODE_NORMAL_P;
    chn_attr.gop_attr.normal_p.ip_qp_delta = 0;

    // 4. 创建通道
    ret = ss_mpi_venc_create_chn(venc_chn, &chn_attr);
    if (ret != TD_SUCCESS) {
        printf("Create venc chn failed! ret = 0x%x\n", ret);
        return ret;
    }

    ot_venc_start_param start_param;
    /* step 2:  start recv venc pictures */
    start_param.recv_pic_num = -1;
    if ((ret = ss_mpi_venc_start_chn(venc_chn, &start_param)) != TD_SUCCESS) {
        ss_mpi_venc_destroy_chn(venc_chn);
        sample_print("ss_mpi_venc_start_recv_pic failed with%#x! \n", ret);
        return TD_FAILURE;
    }

    return TD_SUCCESS;
}

/* 使用VB块送帧 - 改进版本 */
static td_s32 sample_venc_send_yuv_with_vb(ot_venc_chn venc_chn, 
                                          td_u8 *yuv_data, 
                                          ot_size *size, 
                                          td_u64 pts)
{
    td_s32 ret = TD_FAILURE;
    ot_video_frame_info frame_info = {0};
    ot_video_frame *vframe = &frame_info.video_frame;
    ot_vb_blk vb_blk = OT_VB_INVALID_HANDLE;
    td_u32 stride = OT_ALIGN_UP(size->width, 16);
    td_u32 aligned_height = OT_ALIGN_UP(size->height, 2);
    td_u32 aligned_frame_size = stride * aligned_height * 3 / 2;
    td_void *virt_addr = TD_NULL;
    td_phys_addr_t phys_addr = 0;

    // 检查输入参数
    if (yuv_data == TD_NULL || size == TD_NULL) {
        printf("Invalid input parameters!\n");
        return TD_FAILURE;
    }

    if (size->width == 0 || size->height == 0) {
        printf("Invalid size: %dx%d\n", size->width, size->height);
        return TD_FAILURE;
    }

    // 1. 申请VB块
    vb_blk = ss_mpi_vb_get_blk(OT_VB_INVALID_POOL_ID, aligned_frame_size, TD_NULL);
    if (vb_blk == OT_VB_INVALID_HANDLE) {
        printf("Get vb block failed! requested size=%u\n", aligned_frame_size);
        return TD_FAILURE;
    }

    // 2. 获取物理地址
    phys_addr = ss_mpi_vb_handle_to_phys_addr(vb_blk);
    if (phys_addr == 0) {
        printf("Get phys addr failed!\n");
        goto cleanup;
    }

    // 3. 映射虚拟地址
    virt_addr = ss_mpi_sys_mmap(phys_addr, aligned_frame_size);
    if (virt_addr == TD_NULL) {
        printf("ss_mpi_sys_mmap failed!\n");
        goto cleanup;
    }

    // 4. 配置帧信息
    vframe->width = size->width;
    vframe->height = size->height;
    vframe->pixel_format = OT_PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    vframe->video_format = OT_VIDEO_FORMAT_LINEAR;
    vframe->compress_mode = OT_COMPRESS_MODE_NONE;
    vframe->dynamic_range = OT_DYNAMIC_RANGE_SDR8;
    vframe->color_gamut = OT_COLOR_GAMUT_BT601;
    vframe->field = OT_VIDEO_FIELD_FRAME;

    // 5. 设置地址
    vframe->phys_addr[0] = phys_addr;
    vframe->phys_addr[1] = phys_addr + stride * aligned_height;
    vframe->virt_addr[0] = virt_addr;
    vframe->virt_addr[1] = (td_u8 *)virt_addr + stride * aligned_height;

    // 6. 设置跨步
    vframe->stride[0] = stride;
    vframe->stride[1] = stride;

    vframe->pts = pts;
    vframe->time_ref = pts;
    vframe->frame_flag = 0;

    // 7. 拷贝数据
    td_u8 *src_y = yuv_data;
    td_u8 *src_uv = yuv_data + size->width * size->height;
    td_u8 *dst_y = (td_u8 *)vframe->virt_addr[0];
    td_u8 *dst_uv = (td_u8 *)vframe->virt_addr[1];

    td_u32 y_plane_size = size->width * size->height;
    td_u32 uv_plane_size = size->width * size->height / 2;

    // 直接拷贝整个平面(假设数据已经是连续的)
    if (memcpy_s(dst_y, y_plane_size, src_y, y_plane_size) != EOK ||
        memcpy_s(dst_uv, uv_plane_size, src_uv, uv_plane_size) != EOK) {
        printf("Copy yuv data failed!\n");
        goto cleanup;
    }

    ss_mpi_sys_munmap(virt_addr, aligned_frame_size);
    virt_addr = TD_NULL;

    // 8. 设置frame_info的其他成员
    frame_info.pool_id = 0;
    frame_info.mod_id = OT_ID_VENC;

    // 9. 送帧
    ret = ss_mpi_venc_send_frame(venc_chn, &frame_info, 100);
    if (ret != TD_SUCCESS) {
        printf("Send frame to venc chn %d failed! ret=0x%x\n", venc_chn, ret);
    }

    // VB块由VENC管理,不需要在这里释放
    return ret;

cleanup:
    if (virt_addr != TD_NULL) {
        ss_mpi_sys_munmap(virt_addr, aligned_frame_size);
    }

    if (vb_blk != OT_VB_INVALID_HANDLE) {
        ss_mpi_vb_release_blk(vb_blk);
    }

    return TD_FAILURE;
}

/* 简化的送帧函数(测试用) */
static td_s32 sample_venc_send_test_frame(ot_venc_chn venc_chn, ot_size *size)
{
    td_s32 ret;
    static td_u64 frame_count = 0;

    // 创建测试YUV数据(纯色帧)
    static td_u8 yuv_data[1920 * 1080 * 3 / 2];
    static td_bool initialized = TD_FALSE;

    // 初始化YUV数据
    if (!initialized) {
        // Y分量
        td_u32 y_size = size->width * size->height;
        for (td_u32 i = 0; i < y_size; i++) {
            yuv_data[i] = 0x80;  // 灰色
        }

        // UV分量
        td_u32 uv_size = y_size / 2;
        for (td_u32 i = 0; i < uv_size; i++) {
            yuv_data[y_size + i] = 0x80;
        }
        initialized = TD_TRUE;
    }

    // 使用VB块发送
    ret = sample_venc_send_yuv_with_vb(venc_chn, yuv_data, size, frame_count * 30000);
    //ret = sample_venc_send_yuv_user(venc_chn, yuv_data, size, frame_count * 30000);    
    if (ret == TD_SUCCESS) {
        frame_count++;
    }

    return ret;
}

/* 码流获取线程 */
static td_void* sample_venc_get_stream_thread(td_void *args)
{
    ot_venc_chn venc_chn = *(ot_venc_chn*)args;
    ot_venc_stream stream = {0};
    td_s32 ret;
    FILE *fp = fopen("output.h264", "wb");
    td_u32 frame_count = 0;

    if (!fp) {
        printf("Open output file failed!\n");
        return TD_NULL;
    }

    printf("Start stream thread for chn %d\n", venc_chn);

    // 分配pack数组
    ot_venc_pack pack_array[10] = {0};
    stream.pack = pack_array;
    stream.pack_cnt = 10;

    while (!g_sample_venc_exit) {
        // 获取码流
        ret = ss_mpi_venc_get_stream(venc_chn, &stream, 1000);
        if (ret == TD_SUCCESS) {
            // 保存码流
            for (td_u32 i = 0; i < stream.pack_cnt; i++) {
                if (pack_array[i].addr != TD_NULL && pack_array[i].len > 0) {
                    fwrite(pack_array[i].addr, 1, pack_array[i].len, fp);
                    fflush(fp);
                }
            }

            // 检查帧类型
            if (stream.pack_cnt > 0) {
                ot_venc_data_type *data_type = &pack_array[0].data_type;
                if (data_type->h264_type == OT_VENC_H264_NALU_IDR_SLICE ||
                    data_type->h264_type == OT_VENC_H264_NALU_I_SLICE) {
                    printf("I/IDR frame: size=%u, pts=%llu\n", 
                           pack_array[0].len, pack_array[0].pts);
                }
            }

            frame_count++;
            if (frame_count % 30 == 0) {
                printf("Encoded %d frames\n", frame_count);
            }

            // 释放码流
            ss_mpi_venc_release_stream(venc_chn, &stream);
        } else if (ret == OT_ERR_VENC_BUF_EMPTY) {
            usleep(10000);
        } else {
            printf("Get stream failed! ret = 0x%x\n", ret);
            break;
        }
    }

    fclose(fp);
    printf("Stream thread exit, total frames: %d\n", frame_count);

    return TD_NULL;
}

/* 主函数 */
td_s32 sample_venc_direct_encode_test(td_void)
{
    td_s32 ret;
    ot_venc_chn venc_chn = 0;
    ot_size enc_size = {1920, 1080};
    pthread_t stream_thread;
    td_u32 i;

    printf("=== Direct YUV to H.264 Encoding Test ===\n");
    ss_mpi_sys_exit();
    ss_mpi_vb_exit();

    // 0. 信号处理
    signal(SIGINT, sample_venc_handle_sig);

    // 1. 系统初始化
    ot_vb_cfg vb_cfg = {0};
    vb_cfg.max_pool_cnt = 1;
    vb_cfg.common_pool[0].blk_size = enc_size.width * enc_size.height * 3 / 2;
    vb_cfg.common_pool[0].blk_cnt = 200;
    vb_cfg.common_pool[0].remap_mode = OT_VB_REMAP_MODE_NONE;

    ret = ss_mpi_vb_set_cfg(&vb_cfg);
    if (ret != TD_SUCCESS) {
        printf("Set vb cfg failed! ret = 0x%x\n", ret);
        return ret;
    }

    ret = ss_mpi_vb_init();
    if (ret != TD_SUCCESS) {
        printf("VB init failed! ret = 0x%x\n", ret);
        return ret;
    }

    printf("Step 2: Initialize SYS...\n");
    ret = ss_mpi_sys_init();
    if (ret != TD_SUCCESS) {
        printf("SYS Init failed: 0x%x\n", ret);
        ss_mpi_vb_exit();
        return ret;
    }

    // 2. VENC初始化 - 修正:通常不需要显式初始化VENC
    // 直接创建通道即可,系统会自动初始化VENC模块

    // 3. 创建编码通道
    printf("Creating VENC channel...\n");
    ret = sample_venc_create_chn_direct(venc_chn, &enc_size, OT_PT_H264);
    if (ret != TD_SUCCESS) {
        printf("Create VENC channel failed!\n");
        goto EXIT_VB;
    }

    // 4. 启动码流获取线程
    printf("Starting stream thread...\n");
    if (pthread_create(&stream_thread, NULL, sample_venc_get_stream_thread, &venc_chn) != 0) {
        printf("Create thread failed!\n");
        goto EXIT_DESTROY_CHN;
    }

    // 5. 编码循环
    printf("Start encoding... (Press Ctrl+C to stop)\n");
    for (i = 0; i < 300 && !g_sample_venc_exit; i++) {
        ret = sample_venc_send_test_frame(venc_chn, &enc_size);
        if (ret != TD_SUCCESS) {
            printf("Send frame %d failed! ret = 0x%x\n", i, ret);
            exit(-1);
        }

        usleep(33333);  // 30fps

        if (i % 30 == 0) {
            printf("Sent %d frames\n", i);
        }
    }

    printf("Encoding finished\n");

    // 6. 发送结束帧
    ot_video_frame_info end_frame = {0};
    end_frame.video_frame.frame_flag = OT_FRAME_FLAG_SNAP_END;
    ss_mpi_venc_send_frame(venc_chn, &end_frame, 1000);

    // 7. 等待线程结束
    g_sample_venc_exit = TD_TRUE;
    sleep(2);
    pthread_join(stream_thread, NULL);

EXIT_DESTROY_CHN:
    // 8. 清理资源
    printf("Cleaning up...\n");
    ss_mpi_venc_destroy_chn(venc_chn);

    // 注意:没有 ss_mpi_venc_exit() 函数,只需要销毁通道即可

EXIT_VB:
    ss_mpi_sys_exit();
    ss_mpi_vb_exit();

    printf("Test completed!\n");
    return TD_SUCCESS;
}

/* 简单的主函数 */
#ifdef __LITEOS__
td_s32 app_main(td_s32 argc, td_char *argv[])
#else
td_s32 main(td_s32 argc, td_char *argv[])
#endif
{
    td_s32 ret;

    signal(SIGINT, sample_venc_handle_sig);

    ret = sample_venc_direct_encode_test();
    if (ret == TD_SUCCESS) {
        printf("Program exit normally!\n");
    } else {
        printf("Program exit with error!\n");
    }

    return ret;
}
我来回答
回答3个
时间排序
认可量排序

UncleRoderick

61个粉丝

18

问答

4

专栏

20

资料

UncleRoderick 2026-01-19 17:22:54
认可2

大概看了下流程,这个vb在整个程序周期都在使用的,可以考虑在主线程的ss_mpi_sys_init之后申请,在ss_mpi_sys_exit之前释放,至于你其他函数使用到了这个vb,可以考虑封装函数调用,或者传参给线程

Charley王大王
Charley王大王   回复   UncleRoderick  2026-01-19 17:56:10
0

我的困惑点是ss_mpi_vb_get_blk获取到的vb block将数据传给venc进行编码,应该是由VENC模块自己释放,还是在什么地方手动释放?

UncleRoderick
UncleRoderick   回复   Charley王大王  2026-01-19 19:03:02
1

这个vb是你手动获取的,VENC是不会释放的,顶多就是占用/解除占用

或将文件直接拖到这里
悬赏:
E币
网盘
* 网盘链接:
* 提取码:
悬赏:
E币

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
+ 添加网盘链接/附件

Markdown 语法

  • 加粗**内容**
  • 斜体*内容*
  • 删除线~~内容~~
  • 引用> 引用内容
  • 代码`代码`
  • 代码块```编程语言↵代码```
  • 链接[链接标题](url)
  • 无序列表- 内容
  • 有序列表1. 内容
  • 缩进内容
  • 图片![alt](url)
举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

易百纳技术社区