【发布时间】:2020-02-07 07:16:35
【问题描述】:
我正在做一个项目,我想将给定的视频输入流转换为块部分(以便硬件编解码器可以使用它)。该项目在运行 200Mhz 时钟的 STM32 微控制器上运行。
接收到的输入是一个 YCbCr 4:2:2 渐进流,这基本上意味着输入流对于每一行都是这样的:
Size: 32 bit word 32 bit word 32 bit word ...
Component: Cr Y1 Cb Y0 Cr Y1 Cb Y0 Cr Y1 Cb Y0 ...
Bits: 8 8 8 8 8 8 8 8 8 8 8 8 ...
此流需要转换为硬件编解码器使用的块格式。编解码器以特定顺序接受字节数组。目前,我正在使用查找表并写入空数组的图像帧的每 1/8 使用嵌套循环来执行此操作:
定义:
#define ROWS_PER_MCU 8
#define WORDS_PER_MCU 8
#define HORIZONTAL_MCU_PER_INPUTBUFFER 40
#define VERTICAL_MCU_PER_INPUTBUFFER 8
全局变量声明如下:
typedef struct jpegInputbufferLUT
{
uint8_t JPEG_Y_MCU_LUT[256];
uint8_t JPEG_Cb_MCU_422_LUT[256];
uint8_t JPEG_Cr_MCU_422_LUT[256];
}jpegIndexLUT;
jpegIndexLUT jpegInputLUT;
uint8_t jpegInBuffer[81920];
uint32_t rawBuffer[20480];
查找表是这样创建的:
void JPEG_Init_MCU_LUT(void)
{
uint32_t offset;
/*Y LUT */
for(uint32_t i = 0; i < 16; i++)
{
for(j = 0; j < 16; j++)
{
offset = j + (i*8);
if((j>=8) && (i>=8)) offset+= 120;
else if((j>=8) && (i<8)) offset+= 56;
else if((j<8) && (i>=8)) offset+= 64;
jpegInputLUT.JPEG_Y_MCU_LUT[i*16 + j] = offset;
}
}
/*Cb Cr LUT*/
for(uint32_t i = 0; i < 16; i++)
{
for(j = 0; j < 16; j++)
{
offset = i*16 + j;
jpegInputLUT.JPEG_Cb_MCU_422_LUT[offset] = (j/2) + (i*8) + 128;
jpegInputLUT.JPEG_Cr_MCU_422_LUT[offset] = (j/2) + (i*8) + 192;
}
}
}
转换代码:
/* Initialize variables for array conversion */
uint32_t currentMCU = 0;
uint32_t lutOffset = 0;
uint32_t inputOffset = 0;
uint32_t verticalOffset = 0;
/* Convert X rows into MCU blocks for JPEG encoding */
for(uint8_t k = 0; k < VERTICAL_MCU_PER_INPUTBUFFER; k++)
{
for(uint8_t n = 0; n < HORIZONTAL_MCU_PER_INPUTBUFFER; n++)
{
inputOffset = verticalOffset + (n * 8);
lutOffset = 0;
for(uint8_t i = 0; i < ROWS_PER_MCU; i++)
{
for(uint8_t j = 0; j < WORDS_PER_MCU; j++)
{
/* Mask 32 bit according to DCMI input format */
uint32_t rawBufferAddress = inputOffset+j; // Calculate rawBuffer address here so it only has to be calculated once
jpegInBuffer[jpegInputLUT.JPEG_Y_MCU_LUT[lutOffset] + currentMCU] = (rawBuffer[rawBufferAddress] & 0x7F);
jpegInBuffer[jpegInputLUT.JPEG_Cb_MCU_422_LUT[lutOffset] + currentMCU] = ((rawBuffer[rawBufferAddress] >> 7) & 0x7F);
jpegInBuffer[jpegInputLUT.JPEG_Cr_MCU_422_LUT[lutOffset] + currentMCU] = ((rawBuffer[rawBufferAddress] >> 23) & 0x7F);
jpegInBuffer[jpegInputLUT.JPEG_Y_MCU_LUT[lutOffset+1] + currentMCU] = ((rawBuffer[rawBufferAddress] >> 16) & 0x7F);
lutOffset+=2;
}
inputOffset += 320;
}
currentMCU += 256;
}
verticalOffset += 2240;
}
这个转换目前需要我大约 8 毫秒,并且需要完成 8 次。目前这几乎占用了我所有可用的执行时间,因为我试图从我的系统中获得 15 fps。
有没有办法加快速度?我在想也许对输入数组进行排序而不是仅仅写入一个新的缓冲区,但是交换一个数组中的 2 个元素会比将值复制到另一个数组中更快的执行时间吗?
很想听听您对此的想法/想法,
提前致谢!
【问题讨论】:
-
您有一个想法,尝试它,衡量它 - 没有人可以明确告诉您它是否会更快。因此,您真正要寻找的是其他建议。你在什么编译器优化设置下达到了 8ms?确实是什么编译器?这两件事都可能产生重大影响。
-
当我看到像
rawBuffer[rawBufferAddress]这样的东西重复了很多次时,这是设置变量并多次使用该变量的候选项。您是否正在编译完全优化的构建?与往常一样,检查代码的汇编输出,看看有哪些可以简化或改进的地方。 -
@tadman : 是的,但它也是优化器会发现并为您完成工作的那种不变表达式。
-
@tadman : STM32 是 ARM-Cortex,因此受到 GCC 的支持,而 ARMCC - 版本 6 基于 CLANG,v5 是 ARM 专有的 - 都可以胜任。
-
8 ms * 200M = 1.6M 个周期,除以 (8*8*8*40) 得到 78 个周期。似乎太慢了。检查生产的组件类型。
标签: c optimization embedded microcontroller stm32