Marvel Pxa310 Video横屏竖屏切换

 

<!-- /* Font Definitions */ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:黑体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimHei; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:1 135135232 16 0 262144 0;} @font-face {font-family:"/@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 135135232 16 0 262145 0;} @font-face {font-family:"/@黑体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:1 135135232 16 0 262144 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0cm; margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.5pt; mso-bidi-font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:宋体; mso-font-kerning:1.0pt;} h1 {mso-style-next:正文; margin-top:17.0pt; margin-right:0cm; margin-bottom:16.5pt; margin-left:0cm; text-align:justify; text-justify:inter-ideograph; line-height:240%; mso-pagination:lines-together; page-break-after:avoid; mso-outline-level:1; font-size:22.0pt; font-family:"Times New Roman"; mso-font-kerning:22.0pt;} h2 {mso-style-next:正文; margin-top:13.0pt; margin-right:0cm; margin-bottom:13.0pt; margin-left:0cm; text-align:justify; text-justify:inter-ideograph; line-height:173%; mso-pagination:lines-together; page-break-after:avoid; mso-outline-level:2; font-size:16.0pt; font-family:Arial; mso-fareast-font-family:黑体; mso-bidi-font-family:"Times New Roman"; mso-font-kerning:1.0pt;} /* Page Definitions */ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page Section1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:42.55pt; mso-footer-margin:49.6pt; mso-paper-source:0; layout-grid:15.6pt;} div.Section1 {page:Section1;} /* List Definitions */ @list l0 {mso-list-id:734666112; mso-list-type:hybrid; mso-list-template-ids:-1866804122 1008655198 248933464 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l0:level1 {mso-level-text:%1,; mso-level-tab-stop:18.0pt; mso-level-number-position:left; margin-left:18.0pt; text-indent:-18.0pt;} @list l0:level2 {mso-level-number-format:alpha-lower; mso-level-text:"%2/)"; mso-level-tab-stop:39.0pt; mso-level-number-position:left; margin-left:39.0pt; text-indent:-18.0pt;} @list l1 {mso-list-id:1779637185; mso-list-type:hybrid; mso-list-template-ids:-840918712 838602636 67698713 67698715 67698703 67698713 67698715 67698703 67698713 67698715;} @list l1:level1 {mso-level-text:%1,; mso-level-tab-stop:39.0pt; mso-level-number-position:left; margin-left:39.0pt; text-indent:-18.0pt;} ol {margin-bottom:0cm;} ul {margin-bottom:0cm;} -->

                                                                          Marvel Pxa310 Video 横屏竖屏切换

前言

              最近这几天一直在折腾这个横屏切换,公司买了一个破屏,只支持 480 的刷屏,而不支持 800 的刷屏,也就是说我们的屏幕内容是 800X480 的,而屏呢只支持 480X800 ,所以有了旋转这个需求,就是在播放视频的时候,必须要从 800X480 转到 480X800

思路

              Marvel pxa310 没有自己的 DSP 不过有一个图形加速,也就是 M2D ,这玩意的意思就是 memory to device ,实际上,多数用法都是从内存的一个地方按照一定的算法移动到 framebuffer 或者一个中间的内存。基于这个特点,我的想法就是把 mplayer 解码出来的数据利用 M2D 传输到 framebuffer ,这样可以节省一点 CPU 的资源。

 

步骤

              想法其实很简单,但是实际行动中就会有很多的问题。

1,  理解 Mplayer 的架构;

              它的视频输出架构大约是这样的,根据传入的参数 vo XXX 调用 init_best_video_out 函数,这个函数遍历支持的输出 driver ,而这些支持的 driver 是通过一个数组 video_out_drivers 来保存的,不同的输出 driver 只要实现

              typedef struct vo_functions_s

{

              const vo_info_t *info;

              /*

                * Preinitializes driver (real INITIALIZATION)

                *   arg - currently it's vo_subdevice

                *   returns: zero on successful initialization, non-zero on error.

                */

              int (*preinit)(const char *arg);

        /*

         * Initialize (means CONFIGURE) the display driver.

                * params:

         *   width,height: image source size

                *   d_width,d_height: size of the requested window size, just a hint

                *   fullscreen: flag, 0=windowd 1=fullscreen, just a hint

                *   title: window title, if available

                *   format: fourcc of pixel format

         * returns : zero on successful initialization, non-zero on error.

         */

        int (*config)(uint32_t width, uint32_t height, uint32_t d_width,

                                             uint32_t d_height, uint32_t fullscreen, char *title,

                                             uint32_t format);

 

              /*

                * Control interface

                */

              int (*control)(uint32_t request, void *data, ...);

 

        /*

         * Display a new RGB/BGR frame of the video to the screen.

         * params:

                *    src[0] - pointer to the image

         */

        int (*draw_frame)(uint8_t *src[]);

 

        /*

         * Draw a planar YUV slice to the buffer:

                * params:

                *   src[3] = source image planes (Y,U,V)

         *   stride[3] = source image planes line widths (in bytes)

                *   w,h = width*height of area to be copied (in Y pixels)

         *   x,y = position at the destination image (in Y pixels)

         */

        int (*draw_slice)(uint8_t *src[], int stride[], int w,int h, int x,int y);

 

            /*

         * Draws OSD to the screen buffer

         */

        void (*draw_osd)(void);

 

        /*

         * Blit/Flip buffer to the screen. Must be called after each frame!

         */

        void (*flip_page)(void);

 

        /*

         * This func is called after every frames to handle keyboard and

                * other events. It's called in PAUSE mode too!

         */

        void (*check_events)(void);

 

        /*

         * Closes driver. Should restore the original state of the system.

         */

        void (*uninit)(void);

 

}

这个结构体里面的函数指针就可以了。

我们用的是 overlay2 也就是 /dev/fb2 的输出,所以我们实现相应的 preinit config draw_slice 等等函数;而这里最关键的是 draw_slice , 因为我们的视频解码出来的数据是 YUV420 的,所以我们需要实现 draw_slice 否则你就要实现 draw_frame 函数;

2,  理解 draw_slice 相关函数;

              对于 set_fb_overlay_region 函数,它会在 config 函数被调用的时候执行,它所做的事就是:

              a) 设置视频输出格式为 YV12, 也就是 YUV420

                             fb_vinfo.nonstd = (OV2_FMT_YUV420 << 20) ;

                             ioctl(fb_dev_fd , FBIOPUT_VSCREENINFO, &fb_vinfo)

                             这里 fb_dev_fd /dev/fb2 的文件描述符,而 fb_vinfo 里面除了输出格式外还包括了输出的宽度和高度,这里必须要满足驱动的要求,比如我就需要修改这里,因为从硬件的角度来说,屏幕已经是 480X800 了,所以我必须要设置成 480X800 ,虽然,从上层的角度来说需要看成 800X480 (否则内存布局会出错);

              b) 调用 ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo) 取得 fb2 相关的信息;

                             比如:内存大小,每行的像素个数,每个像素所占的字节数;这里我需要修改的是把取得的每行的像素个数强制设成 800 ,因为对上层来说看到的必须是 800X480 ,另外非常重要的是取得 Y U V 各自在 framebuffer 里面的位移和长度,比如我板子上结果就是这样, 0,384000,480000, 长度是 384000 96000 96000 ;这几个数据的作用在于控制了数据写入的目标地址;

              c) 调用 mmap 映射 fb2 framebuffer 空间;

              d )调用 m2d_create_buffer 来创建 M2D 所需的目标地址空间;

                             这一步非常非常的重要,因为这一步让它了解了目的空间的几何架构,也便于它在转化的时候控制换行等:

                             dst_buf[0] = m2d_create_buffer(context, oh, ow, GCU_PXLFMT_INDEXED_8, frame_buffer + y_off);

                            dst_buf[1] = m2d_create_buffer(context, (oh / 2), (ow / 2), GCU_PXLFMT_INDEXED_8, frame_buffer + cb_off);

                            dst_buf[2] = m2d_create_buffer(context, (oh / 2), (ow / 2), GCU_PXLFMT_INDEXED_8, frame_buffer + cr_off);

                             这里的 oh 就是 480 ow 就是 800 ,而 frame_buffer 就是起始地址,而 y_off,cb_off,cr_off 就是分别的 yuv 的位移 0,384000,480000;

              对于 draw_line 函数我只关心和视频输出相关的部分,其它部分先略去,它里面简单的说做了三件事;

a)      分配一个物理连续的内存空间( m2d 要求源和目的都必须是连续的空间,目的这里就是 framebuffer );

b)      把视频解码出来的数据 copy 到刚分配的连续空间里面;

c)       利用 M2D 对源进行放大,或者缩小的操作后把数据放到 framebuffer 里面去;

这里并没有实现旋转,所以需要添加旋转的功能进去;

本来打算在上面的基础上改,但是考虑到它只实现了放大缩小的功能,而并没有实现部分放大,部分缩小的功能,所以,我决定还是重新写一套硬件加速的输出;

 

3,  新的输出实现;

              1 ,调用 m2d_create_buffer 来创建 m2d 所需的源 buffer ,这里还需要设置旋转的度数为 90 度,对于 m2d 的参数来说就是 1

              2 ,调用 gcu_parse 根据源的宽度和高度和目的地决定需要放大,缩小还是旋转等?并由此创建 m2d 所需要的操作子,所谓的操作子也就是关于源和目的地的信息,比如 src 的起始地址,宽度,高度,目的地的起始地址,宽度,高度等, m2d 有了这些信息才能正确进行要求的操作,这里比较关键的是必须要设置好 dst 的宽度和高度,分别为 480 800

              gcu->rotate_times = gcu_create_op(gcu->rot_opr, gcu->src_width, gcu->src_height, gcu->dst_height, gcu->dst_width, 0);                        

              这里非常非常的关键 : 参数的传递一定是按照 src 宽度,高度,目的高度,宽度;比如对于 800X480 的旋转就是 800,480,800,480 ;原因是创建的旋转 buffer 是这么创建的:

              gcu->rot_buf[i] = m2d_alloc_buffer(gcu->context, gcu->src_height[i], gcu->src_width[i], GCU_PXLFMT_INDEXED_8);          

              这里的宽度传入的是 src 的高度,所以对于 800X480 的源它的高度就是 480 ,于是传入的参数就是 (480,800); 也就是说对于中间 buffer 它的几何尺寸就是 480X800 的;

              gcu->resize_times = gcu_create_op(gcu->opr, gcu->dst_height, gcu->dst_width, gcu->dst_width, gcu->dst_height, gcu->rotate_degree);            

             

              这里的 rot_opr 就是将要被设置的操作子,

              3 ,一切就绪后就可以调用 gcu_process 来进行命令的构造了;

                            对于旋转拉伸的设置就是这样了:

                                           /* 设置 m2d src*/

                                           m2d_set_srcbuf0(gcu->context, gcu->src_buf[i]);

                                           /* 设置 m2d dst ,这里是一个中间 buffer*/

                                           m2d_set_dstbuf2(gcu->context, gcu->rot_buf[i]);             

                                          

                                           if(!gcu->rotate_times){

                                                         /* 如果源宽度小于 497 就只需要旋转一次 */

                                                         m2d_rotate_blt(gcu->context, &gcu->rot_opr[i][0], gcu->rotate_degree);

                                           }else{

                                                         /* 如果源宽大大于 497 就要旋转两次了 , 里面的操作子在上一步的时候就设置好了 */

                                                         m2d_rotate_blt(gcu->context, &gcu->rot_opr[i][0], gcu->rotate_degree);

                                                         m2d_rotate_blt(gcu->context, &gcu->rot_opr[i][1], gcu->rotate_degree);

                                           }

                                           //stretch

                                           /* 再次设置源和 dst ,注意这里的源是 rot_buf 也就是经过旋转的数据;

                                           这里的目的是 framebuffer*/

                                           m2d_set_srcbuf0(gcu->context, gcu->rot_buf[i]);

                                           m2d_set_dstbuf2(gcu->context, gcu->dst_buf[i]);             

                                          

                                           if(!gcu->resize_times){/* 如果宽度小于 497, 因为已经是 480X800 得了,所以不需要操作两次,实际上如果源是 800X480 的,是不需要再拉伸了,这里只是考虑普通的情况源是小于 800X480 */

                                                         m2d_stretch_blt(gcu->context, &gcu->opr[i][0], gcu->dst_width[i], gcu->dst_height[i]);

                                           }else{

                                                         m2d_stretch_blt(gcu->context, &gcu->opr[i][0], gcu->dst_width[i]>>1, gcu->dst_height[i]);

                                                         m2d_stretch_blt(gcu->context, &gcu->opr[i][1], gcu->dst_width[i]>>1, gcu->dst_height[i]);

                                           }                          

                                                                      

                            4 ,最后把命令发送到驱动;

                                           m2d_submit(gcu->context); 这个命令才最终 append 到驱动的缓冲,等待驱动取出命令并执行;

                                           至此,基本新的输出架构实现就完了;

 

总结

                             主要的难点就是如何填这些参数,特别是旋转的宽度和高度在大于 497 的时候的设置;

                             理解 M2D 的本质,它只是一个搬运工具,并且自带搬运算法的工具,出问题都是在 src 写到 framebuffer 的时候出问题了,直接把 src 的内存 copy fb2 framebuffer 本质上也是可以工作的,比如:

                             /*added for test*/

                             unsigned char *test=m2d_buff_addr(dst_buf[0]);

                             unsigned char *test_u=m2d_buff_addr(dst_buf[1]);

                             unsigned char *test_v=m2d_buff_addr(dst_buf[2]);

                             for(i = 0; i < h; i ++)

                             {

                                           /*added for test*/

                                           memcpy(test,pSrcY,w);

                                           test+=iYWidth;

                                           /*above is added by wf*/

                             }

                             for(i = 0; i < iCbCrHeight; i ++)

                             {

                                          

                                           /*added by wf for test*/

                                           memcpy(test_u,pSrcCb,iCbCrWidth);

                                           memcpy(test_v,pSrcCr,iCbCrWidth);

                                           test_u+=iCbCrWidth;

                                           test_v+=iCbCrWidth;

                                           /*above is added by wf*/

                             }

                             这样都是可以显示的,只不过没有旋转也没有拉伸。

问答

1,  出现黑屏了,什么都看不到,什么原因?

       有可能是 fb2 没有 enable ,有可能是设置的 m2d 的参数不对,可以通过把 libm2d.so 加上打印信息看看,是不是参数设置不对,特别是宽度和高度;

2,  出现乱七八糟的东西,什么问题?

       有可能是输出的格式的设置的问题,对于视频需要设置成 YUV420 ,也就意味着输入的源数据一定是要 YUV 格式的;创建 buffer 的时候,比如 m2d_create_buffer(context, oh, ow, GCU_PXLFMT_INDEXED_8 , frame_buffer + y_off); 这个参数 GCU_PXLFMT_INDEXED_8 一定要填好。 另外必须要仔细考虑 src 的内存布局和目标的内存布局;

3,  出现重叠,就是一个图像重复几次显示在 LCD 上,什么原因?

       估计是宽度和高度设置有问题,也就是在创建 m2d 的目的 buffer 的时候参数有问题;

 

备注

              作者: wylhistory;

                   联系方式: wylhistory@gmail.com

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值