VENC读取数据时 AO输出哒哒哒哒哒的噪音

Viewed 274

重现步骤

为了让不使用的VENC数据不阻塞随时有可能使用的其他venc通道 专门开了3个线程来分别轮询每一个venc通道的缓冲区
这个线程只是单纯的调用 kd_mpi_venc_get_stream 并立即调用 kd_mpi_venc_release_stream 释放缓冲区
但现在发现每次调用 kd_mpi_venc_get_stream 的时候 AO都会莫名其妙输出一个"哒哒哒"的声音

期待结果和实际结果

软硬件版本信息

01科技开发板 RTOS ONLY

错误日志

没有报错

尝试解决过程

屏蔽掉 kd_mpi_venc_get_stream 和 kd_mpi_venc_release_stream后 这个“哒哒哒”的声音就不会出现了

补充材料

代码如下
image.png

4 Answers

image.png 参考这部分,你的这部分代码实现有问题:venc_stream参数不能不初始化的,pack_cnt变量值都没有设置?kd_mpi_venc_query_status 呢?

意思是 pack_cnt 是一个输入值 是需要我主动输入的值 而不是用来接收kd_mpi_venc_get_stream 返回的pack数量的?

之前我看这个例程的时候 我一直认为调用 kd_mpi_venc_query_status 去查询通道状态的步骤仅仅是为了给venc_stream.pack 动态 malloc使用的 所以 我实现的时候 直接一次性给了一个静态的venc_packs[12] 避免去malloc 这么说这样做是不对的?

是的

/**
 * @file main.c
 * @brief 简化复现代码 - VENC读取导致AO噪音问题
 */

#include "k_acodec_comm.h"
#include "k_audio_comm.h"
#include "k_vb_comm.h"
#include "k_vicap_comm.h"
#include "mpi_adec_api.h"
#include "mpi_aenc_api.h"
#include "mpi_ai_api.h"
#include "mpi_ao_api.h"
#include "mpi_sys_api.h"
#include "mpi_vb_api.h"
#include "mpi_venc_api.h"
#include "mpi_vicap_api.h"
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define VIDEO_WIDTH              1920
#define VIDEO_HEIGHT             1080
#define VIDEO_FPS                25
#define AUDIO_SAMPLE_RATE        8000
#define AUDIO_CHANNELS           1
#define AUDIO_BIT_WIDTH          16
#define AUDIO_FRAME_RATE         25
#define MEM_ALIGN_4K             0x1000
#define MEM_ALIGN_UP(addr, size) (((addr) + ((size) - 1U)) & (~((size) - 1U)))
#define VICAP_BUF_CNT            2
#define VENC_BUF_CNT             3

static struct {
    int ao_inited;
    int venc_ch0_inited;
    int venc_ch1_inited;
    int venc_ch2_inited;
    int vi_inited;
    int vb_inited;
} g_device_state = { 0 };

typedef struct {
    pthread_t       thread;
    volatile int    running;
    pthread_mutex_t lock;
    int             chn_id;
} venc_channel_t;

static venc_channel_t g_venc_channels[3] = { 0 };

static int init_vb()
{
    if (g_device_state.vb_inited)
        return 0;
    k_s32       ret = 0, pool_index = -1;
    k_vb_config vb_config;
    memset(&vb_config, 0, sizeof(vb_config));
    vb_config.max_pool_cnt = 32;
    pool_index++;
    vb_config.comm_pool[pool_index].blk_cnt  = VICAP_BUF_CNT * 3;
    vb_config.comm_pool[pool_index].blk_size = MEM_ALIGN_UP(VIDEO_WIDTH * VIDEO_HEIGHT * 3 / 2, MEM_ALIGN_4K);
    vb_config.comm_pool[pool_index].mode     = VB_REMAP_MODE_NOCACHE;
    pool_index++;
    vb_config.comm_pool[pool_index].blk_cnt  = VENC_BUF_CNT * 3;
    vb_config.comm_pool[pool_index].blk_size = MEM_ALIGN_UP(VIDEO_WIDTH * VIDEO_HEIGHT / 2, MEM_ALIGN_4K);
    vb_config.comm_pool[pool_index].mode     = VB_REMAP_MODE_NOCACHE;
    pool_index++;
    vb_config.comm_pool[pool_index].blk_cnt = K_MAX_AUDIO_FRAME_NUM * 4;
    vb_config.comm_pool[pool_index].blk_size
        = AUDIO_SAMPLE_RATE * AUDIO_CHANNELS * (AUDIO_BIT_WIDTH / 8) / AUDIO_FRAME_RATE * 4;
    vb_config.comm_pool[pool_index].mode = VB_REMAP_MODE_NOCACHE;
    if ((ret = kd_mpi_vb_set_config(&vb_config)) || (ret = kd_mpi_vb_init()))
        return ret;
    g_device_state.vb_inited = 1;
    return 0;
}

static void deinit_vb()
{
    if (g_device_state.vb_inited) {
        kd_mpi_vb_exit();
        g_device_state.vb_inited = 0;
    }
}

static int init_ao()
{
    if (g_device_state.ao_inited)
        return 0;
    k_aio_dev_attr aio_attr;
    memset(&aio_attr, 0, sizeof(aio_attr));
    aio_attr.audio_type                                 = KD_AUDIO_OUTPUT_TYPE_I2S;
    aio_attr.kd_audio_attr.i2s_attr.chn_cnt             = 2;
    aio_attr.kd_audio_attr.i2s_attr.sample_rate         = AUDIO_SAMPLE_RATE;
    aio_attr.kd_audio_attr.i2s_attr.bit_width           = KD_AUDIO_BIT_WIDTH_16;
    aio_attr.kd_audio_attr.i2s_attr.snd_mode            = KD_AUDIO_SOUND_MODE_MONO;
    aio_attr.kd_audio_attr.i2s_attr.mono_channel        = KD_I2S_IN_MONO_RIGHT_CHANNEL;
    aio_attr.kd_audio_attr.i2s_attr.i2s_mode            = K_STANDARD_MODE;
    aio_attr.kd_audio_attr.i2s_attr.frame_num           = AUDIO_FRAME_RATE;
    aio_attr.kd_audio_attr.i2s_attr.point_num_per_frame = AUDIO_SAMPLE_RATE / AUDIO_FRAME_RATE;
    aio_attr.kd_audio_attr.i2s_attr.i2s_type            = K_AIO_I2STYPE_INNERCODEC;
    if (kd_mpi_ao_set_pub_attr(0, &aio_attr) || kd_mpi_ao_enable(0) || kd_mpi_ao_enable_chn(0, 0)) {
        kd_mpi_ao_disable(0);
        return -1;
    }
    g_device_state.ao_inited = 1;
    return 0;
}

static void deinit_ao()
{
    if (g_device_state.ao_inited) {
        kd_mpi_ao_disable_chn(0, 0);
        kd_mpi_ao_disable(0);
        g_device_state.ao_inited = 0;
    }
}

static int init_vi()
{
    if (g_device_state.vi_inited)
        return 0;
    k_vicap_sensor_info sensor_info;
    k_vicap_dev_attr    vi_dev_attr;
    k_vicap_chn_attr    vi_chn_attr;
    memset(&sensor_info, 0, sizeof(sensor_info));
    if (kd_mpi_vicap_get_sensor_info(GC2093_MIPI_CSI2_1920X1080_30FPS_10BIT_LINEAR, &sensor_info))
        return -1;
    memset(&vi_dev_attr, 0, sizeof(vi_dev_attr));
    vi_dev_attr.acq_win.width              = VIDEO_WIDTH;
    vi_dev_attr.acq_win.height             = VIDEO_HEIGHT;
    vi_dev_attr.input_type                 = VICAP_INPUT_TYPE_SENSOR;
    vi_dev_attr.image_pat                  = BAYER_PAT_RGGB;
    vi_dev_attr.pipe_ctrl.data             = 0xFFFFFFFF;
    vi_dev_attr.pipe_ctrl.bits.af_enable   = K_FALSE;
    vi_dev_attr.pipe_ctrl.bits.dnr3_enable = K_FALSE;
    vi_dev_attr.pipe_ctrl.bits.ahdr_enable = K_FALSE;
    vi_dev_attr.cpature_frame              = 0;
    vi_dev_attr.dw_enable                  = K_FALSE;
    vi_dev_attr.dev_enable                 = K_TRUE;
    memcpy(&vi_dev_attr.sensor_info, &sensor_info, sizeof(sensor_info));
    if (kd_mpi_vicap_set_dev_attr(VICAP_DEV_ID_0, vi_dev_attr))
        return -1;
    memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
    vi_chn_attr.out_win.width  = VIDEO_WIDTH;
    vi_chn_attr.out_win.height = VIDEO_HEIGHT;
    vi_chn_attr.crop_enable    = K_FALSE;
    vi_chn_attr.scale_enable   = K_FALSE;
    vi_chn_attr.chn_enable     = K_TRUE;
    vi_chn_attr.pix_format     = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
    vi_chn_attr.buffer_num     = VICAP_BUF_CNT;
    vi_chn_attr.buffer_size    = MEM_ALIGN_UP(VIDEO_WIDTH * VIDEO_HEIGHT * 3 / 2, MEM_ALIGN_4K);
    vi_chn_attr.alignment      = 12;
    vi_chn_attr.fps            = VIDEO_FPS;
    if (kd_mpi_vicap_set_chn_attr(VICAP_DEV_ID_0, VICAP_CHN_ID_0, vi_chn_attr) || kd_mpi_vicap_init(VICAP_DEV_ID_0)
        || kd_mpi_vicap_start_stream(VICAP_DEV_ID_0)) {
        kd_mpi_vicap_deinit(VICAP_DEV_ID_0);
        return -1;
    }
    g_device_state.vi_inited = 1;
    return 0;
}

static void deinit_vi()
{
    if (g_device_state.vi_inited) {
        kd_mpi_vicap_stop_stream(VICAP_DEV_ID_0);
        kd_mpi_vicap_deinit(VICAP_DEV_ID_0);
        g_device_state.vi_inited = 0;
    }
}

static int init_venc(int chn_id)
{
    if (chn_id < 0 || chn_id > 2)
        return -1;
    if ((chn_id == 0 && g_device_state.venc_ch0_inited) || (chn_id == 1 && g_device_state.venc_ch1_inited)
        || (chn_id == 2 && g_device_state.venc_ch2_inited))
        return 0;
    k_venc_chn_attr venc_attr;
    memset(&venc_attr, 0, sizeof(venc_attr));
    venc_attr.venc_attr.type            = (chn_id == 0) ? K_PT_H264 : (chn_id == 1) ? K_PT_H265 : K_PT_JPEG;
    venc_attr.venc_attr.stream_buf_size = MEM_ALIGN_UP(VIDEO_WIDTH * VIDEO_HEIGHT / 2, MEM_ALIGN_4K);
    venc_attr.venc_attr.stream_buf_cnt  = VENC_BUF_CNT;
    venc_attr.venc_attr.pic_width       = VIDEO_WIDTH;
    venc_attr.venc_attr.pic_height      = VIDEO_HEIGHT;
    if (chn_id == 0 || chn_id == 1) {
        venc_attr.venc_attr.profile          = (chn_id == 0) ? VENC_PROFILE_H264_MAIN : VENC_PROFILE_H265_MAIN;
        venc_attr.rc_attr.rc_mode            = K_VENC_RC_MODE_CBR;
        venc_attr.rc_attr.cbr.src_frame_rate = VIDEO_FPS;
        venc_attr.rc_attr.cbr.dst_frame_rate = VIDEO_FPS;
        venc_attr.rc_attr.cbr.bit_rate       = 2048;
    } else {
        venc_attr.rc_attr.rc_mode                    = K_VENC_RC_MODE_MJPEG_FIXQP;
        venc_attr.rc_attr.mjpeg_fixqp.src_frame_rate = VIDEO_FPS;
        venc_attr.rc_attr.mjpeg_fixqp.dst_frame_rate = VIDEO_FPS;
        venc_attr.rc_attr.mjpeg_fixqp.q_factor       = 85;
    }
    if (kd_mpi_venc_create_chn(chn_id, &venc_attr))
        return -2;
    if ((chn_id == 0 || chn_id == 1) && kd_mpi_venc_enable_idr(chn_id, K_TRUE)) {
        kd_mpi_venc_destroy_chn(chn_id);
        return -3;
    }
    if (kd_mpi_venc_start_chn(chn_id)) {
        kd_mpi_venc_destroy_chn(chn_id);
        return -4;
    }
    if (chn_id == 0)
        g_device_state.venc_ch0_inited = 1;
    else if (chn_id == 1)
        g_device_state.venc_ch1_inited = 1;
    else
        g_device_state.venc_ch2_inited = 1;
    return 0;
}

// 复现代码续楼上...


static void deinit_venc(int chn_id)
{
    if (chn_id < 0 || chn_id > 2)
        return;
    int* channel_state = NULL;
    if (chn_id == 0)
        channel_state = &g_device_state.venc_ch0_inited;
    else if (chn_id == 1)
        channel_state = &g_device_state.venc_ch1_inited;
    else
        channel_state = &g_device_state.venc_ch2_inited;
    if (*channel_state) {
        kd_mpi_venc_stop_chn(chn_id);
        kd_mpi_venc_destroy_chn(chn_id);
        *channel_state = 0;
    }
}

static int bind_vi_venc(int src_chn_id, int dst_chn_id)
{
    if (src_chn_id != 0 || dst_chn_id < 0 || dst_chn_id > 2)
        return -1;
    k_mpp_chn src_chn = { .mod_id = K_ID_VI, .dev_id = 0, .chn_id = src_chn_id };
    k_mpp_chn dst_chn = { .mod_id = K_ID_VENC, .dev_id = 0, .chn_id = dst_chn_id };
    return kd_mpi_sys_bind(&src_chn, &dst_chn);
}

static int unbind_vi_venc(int src_chn_id, int dst_chn_id)
{
    if (src_chn_id != 0 || dst_chn_id < 0 || dst_chn_id > 2)
        return -1;
    k_mpp_chn src_chn = { .mod_id = K_ID_VI, .dev_id = 0, .chn_id = src_chn_id };
    k_mpp_chn dst_chn = { .mod_id = K_ID_VENC, .dev_id = 0, .chn_id = dst_chn_id };
    return kd_mpi_sys_unbind(&src_chn, &dst_chn);
}

static void* venc_consumer_thread(void* arg)
{
    venc_channel_t*   ctrl      = (venc_channel_t*)arg;
    k_venc_chn_status status    = { 0 };
    k_venc_stream     stream    = { 0 };
    k_venc_pack       packs[12] = { 0 };
    stream.pack                 = packs;
    while (ctrl->running) {
        if (pthread_mutex_trylock(&ctrl->lock) != 0) {
            usleep(30000);
            continue;
        }
        memset(&status, 0, sizeof(status));
        if (kd_mpi_venc_query_status(ctrl->chn_id, &status) == 0) {
            stream.pack_cnt = status.cur_packs > 0 ? status.cur_packs : 1;
            memset(packs, 0, sizeof(packs));
            if (kd_mpi_venc_get_stream(ctrl->chn_id, &stream, -1) == 0)
                kd_mpi_venc_release_stream(ctrl->chn_id, &stream);
        }
        pthread_mutex_unlock(&ctrl->lock);
        usleep(30000);
    }
    return NULL;
}

static int init_venc_channel_control()
{
    for (int i = 0; i < 3; i++) {
        g_venc_channels[i].chn_id  = i;
        g_venc_channels[i].running = 0;
        if (pthread_mutex_init(&g_venc_channels[i].lock, NULL) != 0) {
            for (int j = 0; j < i; j++)
                pthread_mutex_destroy(&g_venc_channels[j].lock);
            return -1;
        }
    }
    return 0;
}

static void deinit_venc_channel_control()
{
    for (int i = 0; i < 3; i++)
        pthread_mutex_destroy(&g_venc_channels[i].lock);
}

static int start_venc_consumers()
{
    for (int i = 0; i < 3; i++) {
        g_venc_channels[i].running = 1;
        if (pthread_create(&g_venc_channels[i].thread, NULL, venc_consumer_thread, &g_venc_channels[i]) != 0) {
            for (int j = 0; j < i; j++) {
                g_venc_channels[j].running = 0;
                pthread_join(g_venc_channels[j].thread, NULL);
            }
            return -1;
        }
    }
    return 0;
}

static void stop_venc_consumers()
{
    for (int i = 0; i < 3; i++) {
        if (g_venc_channels[i].running) {
            g_venc_channels[i].running = 0;
            pthread_join(g_venc_channels[i].thread, NULL);
        }
    }
}

int main(int argc, char* argv[])
{
    if (init_vb() != 0 || init_ao() != 0 || init_vi() != 0) {
        deinit_vi();
        deinit_ao();
        deinit_vb();
        return -1;
    }
    for (int i = 0; i < 3; i++) {
        if (init_venc(i) != 0) {
            for (int j = 0; j < i; j++)
                deinit_venc(j);
            deinit_vi();
            deinit_ao();
            deinit_vb();
            return -1;
        }
    }
    for (int i = 0; i < 3; i++) {
        if (bind_vi_venc(0, i) != 0) {
            for (int j = 0; j < i; j++)
                unbind_vi_venc(0, j);
            for (int j = 0; j < 3; j++)
                deinit_venc(j);
            deinit_vi();
            deinit_ao();
            deinit_vb();
            return -1;
        }
    }
    if (init_venc_channel_control() != 0 || start_venc_consumers() != 0) {
        deinit_venc_channel_control();
        for (int i = 0; i < 3; i++) {
            unbind_vi_venc(0, i);
            deinit_venc(i);
        }
        deinit_vi();
        deinit_ao();
        deinit_vb();
        return -1;
    }
    printf("系统已启动,AO噪音问题开始复现\n按回车键停止测试...\n");
    getchar();
    stop_venc_consumers();
    deinit_venc_channel_control();
    for (int i = 0; i < 3; i++) {
        unbind_vi_venc(0, i);
        deinit_venc(i);
    }
    deinit_vi();
    deinit_ao();
    deinit_vb();
    return 0;
}

音频播放部分,代码在哪里?从上传的代码里面没有看出来,只看到了初始化ao部分,是否可以把完整的能正常运行的代码发一下?不要发代码片段。

不需要播放 就现在这样单纯初始化AO后 就已经能复现到这个问题了 已经是一个完整的代码了 并不是片段

从这份代码中可以看出来你对这部分功能已经有一定深入的了解。但是存在的一个问题,可能是导致问题的根本原因,期望做如下修改再次测试。
问题:代码中启用了一个sensor的第0路通道,同时与编码器的3路通道绑定,这样做是不合理的。
期望的改进:
1)只创建一路编码器,与sensor的第0路通道,查看是否还有问题。如果有问题,请将修改后的源码发我。
2)如果上述步骤没有问题,请启用sensor的0,1,2共三路通道,分别与编码器的三路通道绑定,这样做才是正确的方式。如果有问题,请将修改后的源码发我。

注:启用sensor的多路通道代码可以参考/src/rtsmart/examples/integrated_poc/smart_ipc,对应文档:https://www.kendryte.com/k230_rtos/zh/main/app_develop_guide/comp/ipc.html

按照你的建议 我进行了相应的修改
1)由于社区太长的代码发布时老出错误代码 我将修改后的代码上传到了如下链接
https://www.showdoc.com.cn/2598839904015998/11558706982524246 烦请复制后在浏览器中打开并查看
2) 本次主要修改的内容有

  • 参考你的建议 启用了sensor的0,1,2共三路通道 并分别与编码器的三路通道绑定
  • 上述修改并运行后 提示VENC没有可用的VB 于是又参考smart_ipc工程 修改了VB的初始化参数

3) 完成上述修改后 AO不再输出“哒哒哒哒哒”的噪声
4) 完成上述修改后 产生了一个新问题

  • 现在程序运行中虽然没有报错 但venc通道 1 2 会在运行大概十几帧后卡死在kd_mpi_venc_get_stream(ctrl->chn_id, &stream, -1) 中 venc通道0没有异常 基于上述异常 我尝试过修改vb的初始化参数 但并没有什么用