宁静以致远
zgf的blog
<2008年10月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

留言簿(15)

随笔分类

随笔档案

文章档案

友情链接

资料收藏

搜索

最新评论

阅读排行榜

评论排行榜

 
VC知识库BLOG   首页  新随笔  联系  聚合  登录 
  随笔-31 文章-8 评论-99 Trackbacks-0

        在公司产品的应用中图像固定是352×288的大小,但可以根据网络的状况调整码流速率。我先用的300k的码流进行测试,统计的时间花费如下:

I帧数:  2     P帧数: 96     无编码帧数:  2
dectime(ms) =8737.00, fps = 11.45, length(bytes) = 1210522
time_decode:3619.00 time_out:5107.00

time_decode 是指解码I帧,P帧等花的时间,time_out是指将解码后的yuv420转换成rgb565花的时间。可以看到,格式转换所花的时间居然比真正解码花的时间还多。狂汗,干脆别叫解码器,该叫格式转换器得了。这个转换部分太刺眼了,首先解决掉它。

       先简化相应的c代码。xvid采用的是查表法,这已经是最快的算法,没什么好改的。我只是根据我们产品的特殊情况减少了一些参数。
#define MK_RGB565(R,G,B) \
 ((MAX(0,MIN(255, R)) << 8) & 0xf800) | \
 ((MAX(0,MIN(255, G)) << 3) & 0x07e0) | \
 ((MAX(0,MIN(255, B)) >> 3) & 0x001f)

void
yv12_to_rgb565_c(uint8_t * x_ptr,uint8_t * y_ptr,int32_t * t_ptr)
{
 int16_t x, y;
 
 uint8_t * u_ptr = y_ptr + 176672;
 uint8_t * v_ptr = u_ptr + 49984;
         
 for (y = 0; y < 288; y+=2)
 {   
  int16_t r[2], g[2], b[2];
  r[0] = r[1] = g[0] = g[1] = b[0] = b[1] = 0;                      
  for (x = 0; x < 352; x+=2)
  {
   int rgb_y;
   int b_u0 = B_U_tab[ u_ptr[0] ];                                                                //1
   int g_uv0 = G_U_tab[ u_ptr[0] ] + G_V_tab[ v_ptr[0] ];                            //2
   int r_v0 = R_V_tab[ v_ptr[0] ];                                                                  //2
 
      rgb_y = RGB_Y_tab[ y_ptr[0] ];                                                             //1
   b[0] = (b[0] & 0x7) + ((rgb_y + b_u0) >> SCALEBITS_OUT);                //2
   g[0] = (g[0] & 0x7) + ((rgb_y - g_uv0) >> SCALEBITS_OUT);                //2
   r[0] = (r[0] & 0x7) + ((rgb_y + r_v0) >> SCALEBITS_OUT);                   //2
   *(uint16_t *) x_ptr =  MK_RGB565(r[0], g[0], b[0]);                                //20-100
   rgb_y = RGB_Y_tab[ y_ptr[1] ];
   b[0] = (b[0] & 0x7) + ((rgb_y + b_u0) >> SCALEBITS_OUT);
  g[0] = (g[0] & 0x7) + ((rgb_y - g_uv0) >> SCALEBITS_OUT);
  r[0] = (r[0] & 0x7) + ((rgb_y + r_v0) >> SCALEBITS_OUT);
   b[0] = ((rgb_y + b_u0) >> SCALEBITS_OUT);
   g[0] = ((rgb_y - g_uv0) >> SCALEBITS_OUT);
   r[0] = ((rgb_y + r_v0) >> SCALEBITS_OUT); 
   *(uint16_t *) (x_ptr+2) = MK_RGB565(r[0], g[0], b[0]);
   
   rgb_y = RGB_Y_tab[ y_ptr[480] ];     
  b[1] = (b[1] & 0x7) + ((rgb_y + b_u0) >> SCALEBITS_OUT);
  g[1] = (g[1] & 0x7) + ((rgb_y - g_uv0) >> SCALEBITS_OUT);
  r[1] = (r[1] & 0x7) + ((rgb_y + r_v0) >> SCALEBITS_OUT);
   *(uint16_t *) (x_ptr+704) =  MK_RGB565(r[1], g[1], b[1]);
  rgb_y = RGB_Y_tab[ y_ptr[481] ];   
  b[1] = (b[1] & 0x7) + ((rgb_y + b_u0) >> SCALEBITS_OUT); 
  g[1] = (g[1] & 0x7) + ((rgb_y - g_uv0) >> SCALEBITS_OUT);
   r[1] = (r[1] & 0x7) + ((rgb_y + r_v0) >> SCALEBITS_OUT);
   
   *(uint16_t *) (x_ptr+706)=  MK_RGB565(r[1], g[1], b[1]);
    
   x_ptr += 4;
   y_ptr += 2;
   u_ptr += 1;
   v_ptr += 1;
  }          
  x_ptr += 704;
  y_ptr += 608;
  u_ptr += 64;
  v_ptr += 64;
 }           
}

      这里主要简化了函数的参数,原来的函数有10个输入参数,
void  yv12_to_rgb565_c(uint8_t * x_ptr,int x_stride,uint8_t * y_src,uint8_t * v_src,uint8_t * u_src, int y_stride, int uv_stride,int width,int height,int vflip);    其中像x_stride参数在我的应用中是固定的,直接省略掉,通过分配连续的缓冲空间将 y_src,v_src,u_src三个数据指针简化成一个。这样做的目的是使函数对应的汇编代码简单。
    
       从汇编级优化可真是头痛。还好ADS编译器调试时可以显示c代码相应的汇编代码。直接copy一份出来在它的基础上修改就简单多了。

        写汇编代码最头痛的是寄存器不够用。arm虽然有37个寄存器,但能使用的很少(r0-r12,r14) 。绞尽脑汁,压榨每一寸空间。x循环内执行次数最多,所以里面的临时变量都要用寄存器存。r[2], g[2], b[2]都是16位的,只需3个寄存器就可以存下。不常用的可以存入堆栈。
        r0查找表地址  r10  y数据  r10  u数据  r11 v数据   r6  bu   r7 gu  r8 gv  r9  RGB_Y
       r3   b[0]    r4   g[0]    r5   r[0]
      上面已经将寄存器用完了,其它的变量如x, y 等都只能用堆栈,使用前出栈,使用后压栈。


       上面c代码的后面注释了每步操作所花的指令条数。很明显MK_RGB565花的指令太多了。更进一步发现,MAX(0,MIN(255, R)) 最耗时间。
下面是编译器给的MAX(0,MIN(255, R)) 汇编代码

30014e24 [0xe1dd60f8] * ldrsh    r6,[r13,#8]             ;从内存中载入数据很慢                    
30014e28 [0xe35600ff]   cmp      r6,#0xff                                                      
30014e2c [0xca000004]   bgt      0x30014e44  ; (yv12_to_rgb565_c + 0xc8)    ;跳转
30014e30 [0xe1dd60f8]   ldrsh    r6,[r13,#8]
30014e34 [0xe3560000]   cmp      r6,#0
30014e38 [0xaa000001]   bge      0x30014e44  ; (yv12_to_rgb565_c + 0xc8)   ;跳转
30014e3c [0xe3a06000]   mov      r6,#0
30014e40 [0xea000005]   b        0x30014e5c  ; (yv12_to_rgb565_c + 0xe0)

        跳转时会清除流水线上后继的5条指令。而编译器给的代码中很不恰当的使用了很多跳转指令,严重的损害了速度。

改进跳转的一种代码如下

     cmp     r12,#0xff
     bgt     point2
     cmp  r12, #0x0
     blt  point1
point11
    .............
    ............

point1
    mov  r12, #0x0
    b  point11
point2   
    mov  r12, #0xff
    b  point11
  
    统计数据发现,超出0-255范围而发生跳转的情况不到1/10 。在这个代码中 如果不超过范围,执行 MAX(0,MIN(255, R)) 只需要 4条指令 ,如果超出范围 则增加到 10-15条。

    后来再看了cmp指令的用法,将代码改进如下:
     cmp      r11,#0xff
     movgt   r11,#0xff
     cmp      r11,#0
     movlt    r11,#0
     这样不管是否超范围,执行MAX(0,MIN(255, R)) 都只需 4条指令。

     尽管改进了代码,计算一个点仍需要执行三次MAX(0,MIN(255, R)) ,也就是12条指令,仍然占据了超过一半的指令。也许有其它办法解决,比如 如果能事先限制输入的数不超过0-255范围,也就不用计算MAX(0,MIN(255, R)) 了, 或者 不管范围直接计算,检查计算出的结果不正确时才检查输入的数的范围。我想了好久都没想出好的办法。
 
      还有,开始时我想避免使用比较指令而将一个32位数限制在0-255范围,论坛上The One老大给了一个很好的算法,但是与上面后来的方法比起来,使用的指令更多,有点得不偿失。怪我学艺不精提出了这个刁钻的问题,还是很感谢老大的帮助。


      还要注意指令的顺序问题。
比如:
     ldr    r1,[r0]                             ldr    r1,[r0]
     ldr    r2,[r0,#1]                        add   r4,r4,r1
     ldr    r3,[r0,#2]                        ldr    r2,[r0,#1]
     add   r4,r4,r1                          add  r4,r4,r2
     add  r4,r4,r2                            ldr    r3,[r0,#2]
     and   r4,r4,r3                           and   r4,r4,r3

     同样的功能,左边比右边要好。因为CPU的流水线有好几级,也要充分利用。


      使用所有能想到的方法后,结果是明显的。采用优化后的代码后,测试时间如下:
dectime(ms) =6048.00, fps = 16.53, length(bytes) = 1210522
time_decode:3617.00 time_out:2417.00

    time_out的时间缩小了一半还多。

    现在200M的主频下300k的码流达到了16.5帧/秒 ,改成300M的CPU后应该能达到25帧/秒。
不过头头说300k的码流太简单,要用1M的码流。唉,只有继续优化其它部分。其它部分还有 idct, vlc解码,还有数据的搬运部分都有比较大的优化空间。

posted on 2006-06-10 02:56 zgf的blog 阅读(3385) 评论(7)  编辑 收藏
Comments
  • # re: mpeg4解码在2410上的实现2 代码优化之yuv->rgb
    HateMath
    Posted @ 2006-06-10 08:53
    路过,关注
  • # re: mpeg4解码在2410上的实现2 代码优化之yuv->rgb
    晓寒
    Posted @ 2006-06-10 16:35
    路过。

    没有看懂。

    没有精力关注。 :P
  • # re: mpeg4解码在2410上的实现2 代码优化之yuv->rgb
    pAnic
    Posted @ 2006-06-10 18:09
    有几个地方:
    1,
    int b_u0 = B_U_tab[ u_ptr[0] ];                                                                //1
       int g_uv0 = G_U_tab[ u_ptr[0] ] + G_V_tab[ v_ptr[0] ];                            //2
       int r_v0 = R_V_tab[ v_ptr[0] ];                                                                  //2
    这边表里面都是有符号数?

    2,
    如果1是有符号数,那后面:
    >> SCALEBITS_OUT之后的结果有可能是错误的,因为有符号数移位并不等同乘除法。(如果我没猜错,这里移位应该是除法的优化。)

    3,
    b[0] = (b[0] & 0x7) + ((rgb_y + b_u0) >> SCALEBITS_OUT); 
    这种写法可能不利于编译器优化,试试
    b[0] &= 0x7; b[0] += ((rgb_y + b_u0) >> SCALEBITS_OUT); 
    不过个人感觉意义不大。
  • # re: mpeg4解码在2410上的实现2 代码优化之yuv->rgb
    lbbbb
    Posted @ 2006-07-09 19:21
    我的xvid 1.1.0
    202M主频
    只做解码,不做yuv to rgb转换
    不做显示
    200k码流,没有b帧,没有1/4象素
    速度只有7.3 fps
    相差好大
    为什么呢?
  • # re: mpeg4解码在2410上的实现2 代码优化之yuv->rgb
    zgf
    Posted @ 2006-07-10 09:16
    你的代码要优化呀,把不用的代码部分都去掉。
  • # to pAnic
    zgf
    Posted @ 2006-07-10 09:33
    B_U_tab,G_U_tab, R_V_tab表中都是有符号的数,目的是将浮点乘法结果预先算出放在表中,转换时查表直接找出结果进行运算。这个结果是系数,根据转换公式正负都可能。

    >> SCALEBITS_OUT 不是优化的除法,是浮点转定点的方法。在初始化B_U_tab,G_U_tab, R_V_tab表时先左移了SCALEBITS_OUT,所以最后要逻辑右移SCALEBITS_OUT 。而不是算数右移。算数右移的符号应该是 >>> .

    初始化B_U_tab,G_U_tab, R_V_tab三个表的代码:


    #define SCALEBITS_OUT 13
    #define FIX_OUT(x) ((uint16_t) ((x) * (1L<<SCALEBITS_OUT) + 0.5))

    #define RGB_Y_OUT 1.164
    #define B_U_OUT 2.018
    #define Y_ADD_OUT 16

    #define G_U_OUT 0.391
    #define G_V_OUT 0.813
    #define U_ADD_OUT 128

    #define R_V_OUT 1.596
    #define V_ADD_OUT 128

    void
    colorspace_init(void)
    {
    int32_t i;

    for (i = 0; i < 256; i++) {
      RGB_Y_tab[i] = FIX_OUT(RGB_Y_OUT) * (i - Y_ADD_OUT);
      B_U_tab[i] = FIX_OUT(B_U_OUT) * (i - U_ADD_OUT);
      G_U_tab[i] = FIX_OUT(G_U_OUT) * (i - U_ADD_OUT);
      G_V_tab[i] = FIX_OUT(G_V_OUT) * (i - V_ADD_OUT);
      R_V_tab[i] = FIX_OUT(R_V_OUT) * (i - V_ADD_OUT);
     }
    }
  • # re: mpeg4解码在2410上的实现2 代码优化之yuv->rgb
    william
    Posted @ 2007-05-31 17:37
    能不能把asm的汇编源代码贴出来,大家研究研究啊!
    我在320*240的ARM9上只能跑到26FPS,现在要达到30FPS,还望指教啊 ,我的邮箱 BlackArrow_007@163.com
标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]