当前位置: 首页 > article >正文

帧率转换原理及读写指针实现

1.为什么要做帧率转换

  因为视频信号在传输过程中,输入和输出的帧率可能不同,例如输出要求固定是60帧/s,而输入是30帧/s,50帧/s,或是75帧/s等等,为了保证图像仍然连续传输,无撕裂现象,那么就需要做帧率转换。
注:
(1)帧率是指每秒钟传输图像的帧数。
(2)对于视频输出,例如显卡输出,通常用刷新率表示,单位Hz,不一定等同于帧率。
(3)撕裂现象是指在高速运动画面中,图像上半部和下半部位置错开或内容不同现象。
在这里插入图片描述

2.帧率转换原理

  通常使用SDRAM,DDR SDRAM等存储设备来完成帧率转换。即一边把图像写入帧缓存,一边从帧缓存中读取数据。
  为保证输出图像无撕裂现象,读操作和写操作必须在不同的帧缓存中,那么至少需要2帧缓存以上,一帧缓存用来读,一帧缓存用来写。在输入输出帧率不相等的情况下,如使用2帧缓存,一定会出现撕裂现象。因此至少需使用3帧缓存才能保证无撕裂现象。
  在输入输出帧率相等的情况下,使用2帧缓存就可以正常显示,甚至可以使用1帧缓存。
  在输入输出帧率不相等的情况下,往往就需要使用3帧缓存来实现。
(1)如果输入帧率大于输出帧率,就采用丢帧的方式降低帧率。
(2)如果输入帧率小于输出帧率,就采用重复帧的方式提高帧率。

3.FPGA帧率转换实现

  使用3帧缓存就是有1帧在写,1帧在读,还要1帧中间过渡,可以用读指针,写指针来表示,读写指针分别指向帧缓存的起始地址,关键就是控制好读写指针的顺序。用一句通俗的话描述就是“谁快谁等”。
图1 写比读快
在这里插入图片描述
图2 读比写快
在这里插入图片描述

  如图,3帧缓存用3个标有数字的方框表示,读指针用rd_pt表示,写指针用wr_pt表示。
(1)读写指针均是按照0,1,2的顺序循环;
(2)当1帧数据读完后,rd_pt就指向下一帧;
(3)当1帧数据写完后,wr_pt就指向下一帧
(4)初始复位后,读写指针都从第0帧开始;
(5)当写比读快时,会通过写入新帧覆盖前一帧方式调节,即输入帧率大于输出帧率,写指针比读指针切换快,如图1,当wr_pt写完要由2切换到0时,发现rd_pt还在0,那么wr_pt就停留在2,数据仍然写入第2帧缓存,把之前写入的数据覆盖掉。
(6)当读比写快时,会通过重复读帧方式调节,即输出帧率大于输入帧率,读指针比写指针切换快,如图2,当rd_pt读完要由0切换到1时,发现wr_pt还在1,那么rd_pt就停留在0,仍然从第0帧读数据,即把第0帧的数据重复1遍。
(7)当读与写速率相同时,即输出帧率等于输入帧率,那么读指针会一直跟在写指针后面,以相同的速率切换。
参考代码:

reg [1:0] wr_pt;
reg [1:0] rd_pt;
//写指针跳转
 always@(posedge dma_clk or negedge rst_n)
  begin
    if(!rst_n) 
      wr_pt  <= 2'd0;
    else 
      begin
        if(vin_vs_falling == 1'b1) // vin_vs_falling输入一帧结束标志
          begin
            case (wr_pt)
              2'd0   :  wr_pt  <= (rd_pt == 2'd1) ? 2'd0 : 2'd1;
              2'd1   :  wr_pt  <= (rd_pt == 2'd2) ? 2'd1 : 2'd2;
              2'd2   :  wr_pt  <= (rd_pt == 2'd2) ? 2'd2 : 2'd0;
              2'd3   :  wr_pt  <= 2'd0;
              default :  wr_pt  <= 2'd0;
            endcase
          end
      end
  end

 //读指针跳转
  always@(posedge dma_clk or negedge rst_n)
  begin
    if(!rst_n)
      rd_pt  <= 2'd0;
    else 
      begin
        if(vout_vs_falling == 1'b1) //vout_vs_falling输出一帧结束标志
          begin
            case (rd_pt)
              2'd0   :  rd_pt  <= (wr_pt == 2'd1) ? 2'd0 : 2'd1;
              2'd1   :  rd_pt  <= (wr_pt == 2'd2) ? 2'd1 : 2'd2;
              2'd2   :  rd_pt  <= (wr_pt == 2'd0) ? 2'd2 : 2'd0;
              2'd3   :  rd_pt  <= 2'd0;
              default :  rd_pt  <= 2'd0;
            endcase
          end
      end
  end

  通过以上方式实现对读写指针的控制,同时也实现了帧率的转换控制。
  至于读写指针和每帧图像起始地址,即基地址对应关系,可以用如下方式实现。

//基地址映射 
  always@(posedge dma_clk or negedge rst_n)
  begin
    if(!rst_n) 
      wr_baseaddr    <= 'd0;
    else
      begin 
        case (wr_pt)
          2'd0   :  wr_baseaddr <= base_addr_i                  ;//IMAGE_SIZE * 0
          2'd1   :  wr_baseaddr <= base_addr_i + IMAGE_SIZE     ;//IMAGE_SIZE * 1
          2'd2   :  wr_baseaddr <= base_addr_i + (IMAGE_SIZE<<1);//IMAGE_SIZE * 2
          2'd3   :  wr_baseaddr <= base_addr_i                  ;//IMAGE_SIZE * 0
          default :  wr_baseaddr <= base_addr_i                  ;//IMAGE_SIZE * 0
        endcase
      end
  end
  
  always@(posedge dma_clk or negedge rst_n)
  begin
    if(!rst_n) 
      rd_baseaddr    <= 'd0;
    else 
      begin
        case (rd_pt)
          2'd0   :  rd_baseaddr <= base_addr_i                  ;//IMAGE_SIZE * 0
          2'd1   :  rd_baseaddr <= base_addr_i + IMAGE_SIZE     ;//IMAGE_SIZE * 1
          2'd2   :  rd_baseaddr <= base_addr_i + (IMAGE_SIZE<<1);//IMAGE_SIZE * 2
          2'd3   :  rd_baseaddr <= base_addr_i                  ;//IMAGE_SIZE * 0
          default :  rd_baseaddr <= base_addr_i                  ;//IMAGE_SIZE * 0
        endcase
      end
  end

http://www.kler.cn/a/579938.html

相关文章:

  • 选型消息队列(MQ):ActiveMQ、RabbitMQ、RocketMQ、Kafka对比
  • 代码随想录算法训练营第六十一天 | 108. 冗余连接 109. 冗余连接II
  • PB:如何获取Excel中的工作表数量
  • unity xnode学习总结
  • 【学习方法】技术开发者的提问智慧:如何高效获得解答?
  • Spring Initializr创建springboot项目,提示java 错误 无效的源发行版:16
  • 基于SpringBoot实现旅游酒店平台功能六
  • Vue3 路由的历史记录 如何不允许浏览器前进后退 在函数中使用路由切换组件 路由的重定向
  • 单链表-代码精简版
  • Pytorch 转向TFConv过程中的卷积转换
  • (每日一题) 力扣 860 柠檬水找零
  • 详解继承、多态、消息(对象间通信)和重载
  • A523 527 pk口控制
  • 【实战ES】实战 Elasticsearch:快速上手与深度实践-5.1.2基于Painless脚本的日志告警
  • GB/T4706.1-2024标准下的UV-C低压汞灯老化试验箱
  • [微服务设计]1_微服务
  • 循环链表 - 使用JavaScript封装
  • 原生iOS集成react-native (react-native 0.65+)
  • Unity Shader教程:Lambert漫反射实现原理解析
  • 通过数据集微调LLM后怎么调用