关闭

屏幕录制H.264视频,AAC音频,MP4复用,LibRTMP实时直播

696人阅读 评论(0) 收藏 举报
分类:

上周完成了屏幕录制的程序,实时抓屏、录音,视频采用H.264压缩,音频采用AAC压缩,复用为MP4格式,这样可以在电脑和iOS安卓的移动设备上直接播放。支持HTML5的播放器都可以放,这是标准格式的好处。抓屏也增加了自动缩放的功能,参考我的上一篇博客。把这几部分的思路都整理一下。

    抓屏,方法很多,直接用bitblt、使用directx、使用mirrordriver、甚至还有用mediaencoder的,我比较了bitblt和directx的方法,也查了很多资料。直觉的理解应该是directx的速度会快一些,但是事实上由于win7系统对bitblt进行了处理,速度与directx抓屏不相上下,代码也简洁很多很多,几行代码就解决,在我的thinkpad T410每秒轻轻松松抓屏100多帧,CPU占用也不高。在有些显卡比较差的机器上,性能会急剧下降,但是这种情况下directx也好不到哪去。单纯从抓屏来说,用bitblt足够了。mediaencoder就算了,太麻烦,还得安装一堆东西。mirrordriver没去仔细看,似乎是用这个驱动可以从镜像里面直接获得变化的屏幕数据,这和我的需求不同,我是需要抓整屏,而不是只要变化的部分。如果不是抓屏,而是做屏幕的远程控制,那可以参考vnc、TightVNC、ReallVNC这些开源的代码,都做得相当好,机制不同。我希望是用标准的格式去压缩整个屏幕的,这点VNC的小伙伴们采用的机制无法实现。

    抓屏代码:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. BitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);    
  2. GetDIBits(hMemDC, bmp, 0, height, bmpBuffer, &bminfo, DIB_RGB_COLORS);  

    抓屏以后,RGB32位的数据要转换为YUV,这个代码如果直接用浮点运算实现,那效率相当低。我用的是xvid里面的一段汇编代码,使用了MMX指令,速度很快。大家可以去参考xvid的代码。话说xvid好几年没更新代码,最近又开始更新了,好奇怪。。。以前做mpeg4的时候,感觉xvid太牛了,编码效率极高,现在被x264取代了。。。扯远了,看看RGB2YUV的代码:

;/**************************************************************************

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. ; *  
  2. ; * mmx colorspace conversions  
  3. ; *  
  4. ; *************************************************************************/  
  5.   
  6. bits 32  
  7.   
  8.   
  9. section .data  
  10.   
  11. %macro cglobal 1   
  12.     %ifdef PREFIX  
  13.         global _%1   
  14.         %define %1 _%1  
  15.     %else  
  16.         global %1  
  17.     %endif  
  18. %endmacro  
  19.   
  20. align 16  
  21.   
  22.   
  23. ;===========================================================================  
  24. ; yuv constants  
  25. ;===========================================================================  
  26.   
  27. %define Y_R     0.257  
  28. %define Y_G     0.504  
  29. %define Y_B     0.098  
  30. %define Y_ADD   16  
  31.   
  32. %define U_R     0.148  
  33. %define U_G     0.291  
  34. %define U_B     0.439  
  35. %define U_ADD   128  
  36.   
  37. %define V_R     0.439  
  38. %define V_G     0.368  
  39. %define V_B     0.071  
  40. %define V_ADD   128  
  41.   
  42.   
  43. ;===========================================================================  
  44. ; multiplication matrices  
  45. ;===========================================================================  
  46.   
  47. ; %define SCALEBITS 8  
  48.   
  49. y_mul   dw      25      ; FIX(Y_B)  
  50.         dw      129     ; FIX(Y_G)  
  51.         dw      66      ; FIX(Y_R)  
  52.         dw      0  
  53.   
  54. u_mul   dw      112     ; FIX(U_B)  
  55.         dw      -74     ; FIX(U_G)  
  56.         dw      -38     ; FIX(U_R)  
  57.         dw      0  
  58.   
  59. v_mul   dw      -18     ; FIX(V_B)  
  60.         dw      -94     ; FIX(V_G)  
  61.         dw      112     ; FIX(V_R)  
  62.         dw      0  
  63.   
  64.   
  65.   
  66. section .text  
  67.   
  68. ;===========================================================================  
  69. ;  
  70. ;   void rgb24_to_yv12_mmx(uint8_t * const y_out,  
  71. ;                       uint8_t * const u_out,  
  72. ;                       uint8_t * const v_out,  
  73. ;                       const uint8_t * const src,  
  74. ;                       const uint32_t width,  
  75. ;                       const uint32_t height,  
  76. ;                       const uint32_t stride)  
  77. ;  
  78. ; always flips  
  79. ;  
  80. ;===========================================================================  
  81.   
  82. align 16  
  83. cglobal rgb24_to_yv12_mmx  
  84. rgb24_to_yv12_mmx  
  85.   
  86.         push ebx  
  87.         push ecx  
  88.         push esi  
  89.         push edi              
  90.         push ebp            ; STACK BASE = 20  
  91.   
  92.         ; global consants  
  93.   
  94.         mov eax, [esp + 20 + 28]    ; stride  
  95.         mov ecx, [esp + 20 + 20]    ; width  
  96.         mov ebx, eax  
  97.         sub ebx, ecx                  
  98.         shr ebx, 1                  ; ebx = (stride-width) / 2;  
  99.         push ebx                    ; [esp + 20] = uv_dif  
  100.                             ; STACK BASE = 24  
  101.   
  102.         add eax, eax  
  103.         sub eax, ecx                ; eax = 2*stride - width  
  104.         push eax                    ; [esp + 16] = y_dif  
  105.                             ; STACK BASE = 28  
  106.   
  107.         mov ebx, ecx                ;   
  108.         shr ebx, 1                  ;  
  109.         push ebx                    ; [esp + 12] = width/2  
  110.                             ; STACK BASE = 32  
  111.   
  112.         mov edx, ecx  
  113.         add ecx, edx  
  114.         add ecx, edx                ; ecx = 3*width   (use 4 for rgb32)  
  115.         push ecx                    ; [esp + 8] = width3  
  116.                             ; STACK BASE = 36  
  117.   
  118.         mov edx, ecx  
  119.         add edx, ecx  
  120.         add edx, ecx                ; edx = 3*width3  
  121.         push edx                    ; [esp + 4] = src_dif  
  122.                             ; STACK BASE = 40  
  123.   
  124.         mov esi, [esp + 40 + 16]    ; src  
  125.         mov ebp, [esp + 40 + 24]    ; eax = height  
  126.         mov eax, ebp  
  127.         sub eax, 2  
  128.         mul ecx  
  129.         add esi, eax                ; src += (height-2) * width3  
  130.   
  131.         mov edi, [esp + 40 + 4]     ; y_out  
  132.         mov ecx, [esp + 40 + 8]     ; u_out  
  133.         mov edx, [esp + 40 + 12]    ; v_out  
  134.         movq mm7, [y_mul]         
  135.   
  136.         shr ebp, 1              ; ebp = height / 2  
  137.         push ebp                ; [esp+0] = tmp  
  138.                                 ; STACK BASE = 44  
  139.   
  140. .yloop  
  141.         mov ebp, [esp + 12]     ; ebp = width /2   
  142.   
  143. .xloop  
  144.             ; y_out  
  145.   
  146.             mov ebx, [esp + 8]          ; ebx = width3  
  147.   
  148.             pxor mm4, mm4  
  149.             pxor mm5, mm5  
  150.             movd mm0, [esi]         ; src[0...]  
  151.             movd mm2, [esi+ebx]     ; src[width3...]  
  152.             punpcklbw mm0, mm4      ; [  |b |g |r ]  
  153.             punpcklbw mm2, mm5      ; [  |b |g |r ]  
  154.             movq mm6, mm0           ; = [  |b4|g4|r4]  
  155.             paddw mm6, mm2          ; +[  |b4|g4|r4]  
  156.             pmaddwd mm0, mm7        ; *= Y_MUL  
  157.             pmaddwd mm2, mm7        ; *= Y_MUL  
  158.             movq mm4, mm0           ; [r]  
  159.             movq mm5, mm2           ; [r]  
  160.             psrlq mm4, 32           ; +[g]  
  161.             psrlq mm5, 32           ; +[g]  
  162.             paddd mm0, mm4          ; +[b]  
  163.             paddd mm2, mm5          ; +[b]  
  164.   
  165.             pxor mm4, mm4  
  166.             pxor mm5, mm5  
  167.             movd mm1, [esi+3]       ; src[4...]  
  168.             movd mm3, [esi+ebx+3]   ; src[width3+4...]  
  169.             punpcklbw mm1, mm4      ; [  |b |g |r ]  
  170.             punpcklbw mm3, mm5      ; [  |b |g |r ]  
  171.             paddw mm6, mm1          ; +[  |b4|g4|r4]  
  172.             paddw mm6, mm3          ; +[  |b4|g4|r4]  
  173.             pmaddwd mm1, mm7        ; *= Y_MUL  
  174.             pmaddwd mm3, mm7        ; *= Y_MUL  
  175.             movq mm4, mm1           ; [r]  
  176.             movq mm5, mm3           ; [r]  
  177.             psrlq mm4, 32           ; +[g]  
  178.             psrlq mm5, 32           ; +[g]  
  179.             paddd mm1, mm4          ; +[b]  
  180.             paddd mm3, mm5          ; +[b]  
  181.   
  182.             mov ebx, [esp + 44 + 28]    ; stride  
  183.   
  184.             movd eax, mm0  
  185.             shr eax, 8  
  186.             add eax, Y_ADD  
  187.             mov [edi + ebx], al  
  188.   
  189.             movd eax, mm1  
  190.             shr eax, 8  
  191.             add eax, Y_ADD  
  192.             mov [edi + ebx + 1], al  
  193.   
  194.             movd eax, mm2  
  195.             shr eax, 8  
  196.             add eax, Y_ADD  
  197.             mov [edi], al  
  198.   
  199.             movd eax, mm3  
  200.             shr eax, 8  
  201.             add eax, Y_ADD  
  202.             mov [edi + 1], al  
  203.   
  204.             ; u_out, v_out  
  205.   
  206.             movq mm0, mm6           ; = [  |b4|g4|r4]  
  207.             pmaddwd mm6, [v_mul]        ; *= V_MUL  
  208.             pmaddwd mm0, [u_mul]        ; *= U_MUL  
  209.             movq mm1, mm0  
  210.             movq mm2, mm6  
  211.             psrlq mm1, 32  
  212.             psrlq mm2, 32  
  213.             paddd mm0, mm1  
  214.             paddd mm2, mm6  
  215.   
  216.             movd eax, mm0  
  217.             shr eax, 10  
  218.             add eax, U_ADD  
  219.             mov [ecx], al  
  220.   
  221.             movd eax, mm2  
  222.             shr eax, 10  
  223.             add eax, V_ADD  
  224.             mov [edx], al  
  225.   
  226.             add esi, 2 * 3          ; (use 4 for rgb32)  
  227.             add edi, 2  
  228.             inc ecx  
  229.             inc edx  
  230.   
  231.             dec ebp  
  232.             jnz near .xloop  
  233.   
  234.         sub esi, [esp + 4]          ; src  -= src_dif  
  235.         add edi, [esp + 16]         ; y_out += y_dif  
  236.         add ecx, [esp + 20]         ; u_out += uv_dif  
  237.         add edx, [esp + 20]         ; v_out += uv_dif  
  238.   
  239.         dec dword [esp+0]  
  240.         jnz near .yloop  
  241.   
  242.         emms  
  243.   
  244.         add esp, 24  
  245.         pop ebp  
  246.         pop edi  
  247.         pop esi  
  248.         pop ecx  
  249.         pop ebx  
  250.   
  251.         ret  
  252.   
  253.   
  254.   
  255. ;===========================================================================  
  256. ;  
  257. ;   void rgb32_to_yv12mmx(uint8_t * const y_out,  
  258. ;                       uint8_t * const u_out,  
  259. ;                       uint8_t * const v_out,  
  260. ;                       const uint8_t * const src,  
  261. ;                       const uint32_t width,  
  262. ;                       const uint32_t height,  
  263. ;                       const uint32_t stride)  
  264. ;  
  265. ; always flips  
  266. ;  
  267. ;===========================================================================  
  268.   
  269. align 16  
  270. cglobal rgb32_to_yv12_mmx  
  271. rgb32_to_yv12_mmx  
  272.   
  273.         push ebx  
  274.         push ecx  
  275.         push esi  
  276.         push edi              
  277.         push ebp            ; STACK BASE = 20  
  278.   
  279.         ; global consants  
  280.   
  281.         mov eax, [esp + 20 + 28]    ; stride  
  282.         mov ecx, [esp + 20 + 20]    ; width  
  283.         mov ebx, eax  
  284.         sub ebx, ecx                  
  285.         shr ebx, 1                  ; ebx = (stride-width) / 2;  
  286.         push ebx                    ; [esp + 20] = uv_dif  
  287.                             ; STACK BASE = 24  
  288.   
  289.         add eax, eax  
  290.         sub eax, ecx                ; eax = 2*stride - width  
  291.         push eax                    ; [esp + 16] = y_dif  
  292.                             ; STACK BASE = 28  
  293.   
  294.         mov ebx, ecx                ;   
  295.         shr ebx, 1                  ;  
  296.         push ebx                    ; [esp + 12] = width/2  
  297.                             ; STACK BASE = 32  
  298.   
  299.         mov edx, ecx  
  300.         shl ecx, 2                  ; ecx = 4*width   (use 4 for rgb32)  
  301.         push ecx                    ; [esp + 8] = width4  
  302.                             ; STACK BASE = 36  
  303.   
  304.         mov edx, ecx  
  305.         add edx, ecx  
  306.         add edx, ecx                ; edx = 3*width4  
  307.         push edx                    ; [esp + 4] = src_dif  
  308.                             ; STACK BASE = 40  
  309.   
  310.         mov esi, [esp + 40 + 16]    ; src  
  311.         mov ebp, [esp + 40 + 24]    ; eax = height  
  312.         mov eax, ebp  
  313.         sub eax, 2  
  314.         mul ecx  
  315.         add esi, eax                ; src += (height-2) * width4  
  316.   
  317.         mov edi, [esp + 40 + 4]     ; y_out  
  318.         mov ecx, [esp + 40 + 8]     ; u_out  
  319.         mov edx, [esp + 40 + 12]    ; v_out  
  320.         movq mm7, [y_mul]         
  321.   
  322.         shr ebp, 1              ; ebp = height / 2  
  323.         push ebp                ; [esp+0] = tmp  
  324.                                 ; STACK BASE = 44  
  325.   
  326. .yloop  
  327.         mov ebp, [esp + 12]     ; ebp = width /2   
  328.   
  329. .xloop  
  330.             ; y_out  
  331.   
  332.             mov ebx, [esp + 8]          ; ebx = width4  
  333.   
  334.             pxor mm4, mm4  
  335.             movq mm0, [esi]         ; src[4...       |0...     ]  
  336.             movq mm2, [esi+ebx]     ; src[width4+4...|width4...]  
  337.             movq mm1, mm0  
  338.             movq mm3, mm2  
  339.             punpcklbw mm0, mm4      ; [  |b |g |r ]  
  340.             punpcklbw mm2, mm4      ; [  |b |g |r ]  
  341.             punpckhbw mm1, mm4      ; [  |b |g |r ]  
  342.             punpckhbw mm3, mm4      ; [  |b |g |r ]  
  343.   
  344.             movq mm6, mm0           ; = [  |b4|g4|r4]  
  345.             paddw mm6, mm2          ; +[  |b4|g4|r4]  
  346.             pmaddwd mm0, mm7        ; *= Y_MUL  
  347.             pmaddwd mm2, mm7        ; *= Y_MUL  
  348.             movq mm4, mm0           ; [r]  
  349.             movq mm5, mm2           ; [r]  
  350.             psrlq mm4, 32           ; +[g]  
  351.             psrlq mm5, 32           ; +[g]  
  352.             paddd mm0, mm4          ; +[b]  
  353.             paddd mm2, mm5          ; +[b]  
  354.   
  355.             paddw mm6, mm1          ; +[  |b4|g4|r4]  
  356.             paddw mm6, mm3          ; +[  |b4|g4|r4]  
  357.             pmaddwd mm1, mm7        ; *= Y_MUL  
  358.             pmaddwd mm3, mm7        ; *= Y_MUL  
  359.             movq mm4, mm1           ; [r]  
  360.             movq mm5, mm3           ; [r]  
  361.             psrlq mm4, 32           ; +[g]  
  362.             psrlq mm5, 32           ; +[g]  
  363.             paddd mm1, mm4          ; +[b]  
  364.             paddd mm3, mm5          ; +[b]  
  365.   
  366.             mov ebx, [esp + 44 + 28]    ; stride  
  367.   
  368.             movd eax, mm0  
  369.             shr eax, 8  
  370.             add eax, Y_ADD  
  371.             mov [edi + ebx], al  
  372.   
  373.             movd eax, mm1  
  374.             shr eax, 8  
  375.             add eax, Y_ADD  
  376.             mov [edi + ebx + 1], al  
  377.   
  378.             movd eax, mm2  
  379.             shr eax, 8  
  380.             add eax, Y_ADD  
  381.             mov [edi], al  
  382.   
  383.             movd eax, mm3  
  384.             shr eax, 8  
  385.             add eax, Y_ADD  
  386.             mov [edi + 1], al  
  387.   
  388.             ; u_out, v_out  
  389.   
  390.             movq mm0, mm6           ; = [  |b4|g4|r4]  
  391.             pmaddwd mm6, [v_mul]        ; *= V_MUL  
  392.             pmaddwd mm0, [u_mul]        ; *= U_MUL  
  393.             movq mm1, mm0  
  394.             movq mm2, mm6  
  395.             psrlq mm1, 32  
  396.             psrlq mm2, 32  
  397.             paddd mm0, mm1  
  398.             paddd mm2, mm6  
  399.   
  400.             movd eax, mm0  
  401.             shr eax, 10  
  402.             add eax, U_ADD  
  403.             mov [ecx], al  
  404.   
  405.             movd eax, mm2  
  406.             shr eax, 10  
  407.             add eax, V_ADD  
  408.             mov [edx], al  
  409.   
  410.             add esi, 2 * 4          ; (use 4 for rgb32)  
  411.             add edi, 2  
  412.             inc ecx  
  413.             inc edx  
  414.   
  415.             dec ebp  
  416.             jnz near .xloop  
  417.   
  418.         sub esi, [esp + 4]          ; src  -= src_dif  
  419.         add edi, [esp + 16]         ; y_out += y_dif  
  420.         add ecx, [esp + 20]         ; u_out += uv_dif  
  421.         add edx, [esp + 20]         ; v_out += uv_dif  
  422.   
  423.         dec dword [esp+0]  
  424.         jnz near .yloop  
  425.   
  426.         emms  
  427.   
  428.         add esp, 24  
  429.         pop ebp  
  430.         pop edi  
  431.         pop esi  
  432.         pop ecx  
  433.         pop ebx  
  434.   
  435.         ret  

    转换为YUV数据以后,就是压缩了。用H.264,基本上都是x264了,开源的代码,现在速度也挺快,交叉编译生成DLL直接调用就行。这里参数设置很重要,帧率我做的是每分钟1帧到25x60帧可调,注意是每分钟不是秒。x264大家都很熟悉,不多说了。

    音频部分比较简单了,设定采样率,用acm采集,然后aac压缩。AAC也有开源的LibFAAC代码可以使用。

    264文件和AAC文件生成以后,复用的方法有很多,简单起见,可以用ffmpeg或其他的软件进行复用,注意别让ffmpeg进行二次编码压缩,否则速度慢而且质量损失。

    完成的程序,测了一下,1920x1980的分辨率,自动缩放为1280x720,每秒1帧,录制10分钟的mp4文件大概是4M多,画面清晰,声音清晰,达到预期目标了。接下来的工作是实时直播了。考虑到兼容性和流媒体的特点,使用rtmp协议,而没有用rtsp。用rtmp的优点就是接收端用flash可以播放,不需要安装其他插件了。

    rtmp协议是不开放的,但是也难不倒我们,librtmp是开源的,做的很好。但是移植到windows平台编译还是费了一些周折。要注意的几点:

1、在c/c++预处理器加上NO_CRYPTO,不编ssl部分,不需要加密

2、rtmp.c文件里面,注释掉几行代码

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //#ifdef _DEBUG  
  2. //extern FILE *netstackdump;  
  3. //extern FILE *netstackdump_read;  
  4. //#endif  
  5. //#ifdef _DEBUG  
  6. //      fwrite(ptr, 1, nBytes, netstackdump_read);  
  7. //#endif  
  8. //#ifdef _DEBUG  
  9. //  fwrite(buf, 1, len, netstackdump);  
  10. //#endif  

3、编译时需要链接ws2_32.lib库

    编译以后,生成dll,在主程序里面调用,基本的初始化代码:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /*分配与初始化*/  
  2. rtmp = RTMP_Alloc();  
  3. RTMP_Init(rtmp);  
  4.    
  5. /*设置URL*/  
  6. if (RTMP_SetupURL(rtmp,rtmp_url) == FALSE) {  
  7.     log(LOG_ERR,"RTMP_SetupURL() failed!");  
  8.     RTMP_Free(rtmp);  
  9.     return -1;  
  10. }  
  11.    
  12. /*设置可写,即发布流,这个函数必须在连接前使用,否则无效*/  
  13. RTMP_EnableWrite(rtmp);  
  14.    
  15. /*连接服务器*/  
  16. if (RTMP_Connect(rtmp, NULL) == FALSE) {  
  17.     log(LOG_ERR,"RTMP_Connect() failed!");  
  18.     RTMP_Free(rtmp);  
  19.     return -1;  
  20. }   
  21.    
  22. /*连接流*/  
  23. if (RTMP_ConnectStream(rtmp,0) == FALSE) {  
  24.     log(LOG_ERR,"RTMP_ConnectStream() failed!");  
  25.     RTMP_Close(rtmp);  
  26.     RTMP_Free(rtmp);  
  27.     return -1;  
  28. }  

    然后在x264_encocer_encode编码一帧以后,调用rtmp的函数进行发送。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. size = x264_encoder_encode(cx->hd,&nal,&n,pic,&pout);  
  2.    
  3. int i,last;  
  4. for (i = 0,last = 0;i < n;i++) {  
  5.     if (nal[i].i_type == NAL_SPS) {  
  6.    
  7.         sps_len = nal[i].i_payload-4;  
  8.         memcpy(sps,nal[i].p_payload+4,sps_len);  
  9.    
  10.     } else if (nal[i].i_type == NAL_PPS) {  
  11.    
  12.         pps_len = nal[i].i_payload-4;  
  13.         memcpy(pps,nal[i].p_payload+4,pps_len);  
  14.    
  15.         /*发送sps pps*/  
  16.         send_video_sps_pps();      
  17.    
  18.     } else {  
  19.    
  20.         /*发送普通帧(剩下全部)*/  
  21.         send_rtmp_video(nal[i].p_payload,size-last)  
  22.         break;  
  23.     }  
  24.     last += nal[i].i_payload;  
  25. }  

    发送的代码就不贴了,就是填充rtmp packet的内容。调试的时候发现创建socket失败,折腾半天最后发现是没有调用WSAStartup,低级错误。。。发送的数据流,用Flex air写了个接收程序,也有问题,air3.1可以正常接收,air3.9就不行。好吧。。。今天继续看代码吧,找找原因。音频的发送程序类似,有空再加。

    这个程序将会是我们即将发布的轻录播的一部分,这几天把其他代码完成考虑把程序共享出来。欢迎大家拍砖。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:338397次
    • 积分:5063
    • 等级:
    • 排名:第5512名
    • 原创:99篇
    • 转载:421篇
    • 译文:7篇
    • 评论:26条