使用ffmedia库正常运行一段时间后画面概率出现闪烁/花屏

固件类型:官方提供的固件
固件文件名称:ROC-RK3568-PC-TF-HDMI-MIPI-RK628_Ubuntu20.04-r240_
固件下载地址:百度网盘 请输入提取码
Log日志:log.zip

本帖最后由 MaybeNot 于 2024-10-21 18:44 编辑

使用ffmedia对本地mp4视频文件进行mpp解码+rga颜色空间转换和缩放,将获取到的rgb数据通过opencv显示,发现在正常执行一段时间后(从几分钟到半小时不等),概率出现画面异常,如下图所示,且画面出现异常时没有报错信息。

firefly@firefly:~/ymplayer/test/ffmedia/demo$ sudo ./ymtest /home/firefly/material/127.mp4
Firefly FFMedia: v2.3.1
INFO: 16ModuleFileReader: init: Get Video Resolution( 140 x 1080 )
[INFO] (2024-10-21 18:37:00) (ymtest.cpp:401) source_module_output_para.width=140, height=1080
rga_api version 1.3.2_[0] ( build: 2024-08-08 03:16:34 base: )
==================Pipe===================
ModuleFileReader (H264 140x1080)
   |--->ModuleMppDec (NV12 140x1080)
           |--->ModuleRga (RGB24 384x2960)

INFO: 10MppDecoder: getTimeoutSample: 0x55cbd9e300 frame info changed 1 error 0 discard 0

代码大致如下:之所以不用drm显示而是用opencv是为了测试得到的数据画面是否正常,若用drm display的话无法设置384x2960(是16对齐)的宽高。不知道对于宽高差距较大是否有问题,需不需要调节setBufferCount的大小呢?

void callback_external(void* _ctx, shared_ptr buffer)
{
External_ctx* ctx = static_cast<External_ctx*>(_ctx);
shared_ptr module = ctx->module;

if (buffer == NULL || buffer->getMediaBufferType() != BUFFER_TYPE_VIDEO)
    return;

shared_ptr<VideoBuffer> buf = static_pointer_cast<VideoBuffer>(buffer);
void *data = buf->getActiveData();
uint32_t size = buf->getActiveSize();
pthread_mutex_lock(&rgb_data_ctx.mutex);
while (rgb_data_ctx.count == VBUFFER_SIZE) {

    pthread_cond_wait(&rgb_data_ctx.not_full, &rgb_data_ctx.mutex);
}
memcpy(rgb_data_ctx.rgb_buf[(rgb_data_ctx.in % VBUFFER_SIZE)].rgb_frame, (uchar *)data, size);
rgb_data_ctx.in = (rgb_data_ctx.in + 1) % VBUFFER_SIZE;
rgb_data_ctx.count++;
pthread_cond_signal(&rgb_data_ctx.not_empty);
pthread_mutex_unlock(&rgb_data_ctx.mutex);

}

void process_rgb_frame(void *_ctx)
{

while (true){
    pthread_mutex_lock(&rgb_data_ctx.mutex);
    if (rgb_data_ctx.count == 0 && rgb_data_ctx.done == 1){
        pthread_mutex_unlock(&rgb_data_ctx.mutex);
        log_info("done, break");
        break;
    }

    while(rgb_data_ctx.count == 0 && rgb_data_ctx.done == 0){
        pthread_cond_wait(&rgb_data_ctx.not_empty, &rgb_data_ctx.mutex);
    }

    void *data = rgb_data_ctx.rgb_buf[rgb_data_ctx.out].rgb_frame;
    if(rgb_data_ctx.count > 0)
        rgb_data_ctx.count--;
    rgb_data_ctx.out = (rgb_data_ctx.out+1) % VBUFFER_SIZE;

    cv::Mat mat(cv::Size(384, 2960), CV_8UC3, data);
    cv::imshow("display window", mat);
    cv::waitKey(1);

    pthread_cond_signal(&rgb_data_ctx.not_full);
    pthread_mutex_unlock(&rgb_data_ctx.mutex);
}

for(int i=0; i<VBUFFER_SIZE; i++){
    free(rgb_data_ctx.rgb_buf[i].rgb_frame);
    rgb_data_ctx.rgb_buf[i].rgb_frame = NULL;
    rgb_data_ctx.rgb_buf[i].rgb_ts = 0;
}
return;

}

int start_instance(External_ctx* ctx)
{
int ret;
DemoConfig* inst_conf = &(ctx->config);

file_reader->setProductor(NULL);
file_reader->setBufferCount(20);
ret = file_reader->init();

shared_ptr<ModuleMppDec> dec = make_shared<ModuleMppDec>(inst_conf->input_image_para);
dec->setProductor(file_reader);
dec->setBufferCount(10);
ret = dec->init();
if (ret < 0) {
    ff_error("Dec init failed\n");
    // goto FAILED;
}

shared_ptr<ModuleRga> rga = make_shared<ModuleRga>(inst_conf->output_image_para, inst_conf->rotate);
rga->setProductor(dec);
rga->setBufferCount(2);
ret = rga->init();
if (ret < 0) {
    ff_error("rga init failed\n");
    // goto FAILED;
}

ctx->module = rga;
ctx->rga_buffer = rga->newModuleMediaBuffer();    // 默认为cacheable的buffer
rga->setOutputDataCallback(ctx, callback_external);

std::thread frame_processor(process_rgb_frame, ctx);
frame_processor.detach();

clock_gettime(CLOCK_MONOTONIC, &ctx->start);
file_reader->start();
file_reader->dumpPipe();

while (true) {
    int played_cnt = 0;
    if(STATUS_EOS == file_reader->getModuleStatus()){   // 获取当前组件工作状态
        log_info("STATUS_EOS, end of stream. cnt = %d", played_cnt++);
        goto STOP;
    }
    usleep(5000);
}

}

tchip_askquestions
log.zip (646 Bytes)

1.可以更新版本试试
2.drm在init之后可以通过setPlaneRect或setWindowRect控制显示画面大小。
3.回调传递进来的buffer先调用flushDrmBuf,将数据从cache刷下来。
4. 像这种提前申请了videobuffer的,可以用rga模块导出接口交换buf,避免拷贝。