zoukankan      html  css  js  c++  java
  • 屏幕录制H.264视频,AAC音频,MP4复,LibRTMP现场活动

        上周完成了一个屏幕录制节目,实时屏幕捕获、记录,视频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的小伙伴们採用的机制无法实现。

        抓屏代码:

    BitBlt(hMemDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);	
    GetDIBits(hMemDC, bmp, 0, height, bmpBuffer, &bminfo, DIB_RGB_COLORS);

        抓屏以后,RGB32位的数据要转换为YUV,这个代码假设直接用浮点运算实现,那效率相当低。我用的是xvid里面的一段汇编代码,使用了MMX指令。速度非常快。

    大家能够去參考xvid的代码。话说xvid好几年没更新代码,近期又開始更新了。好奇怪。。。曾经做mpeg4的时候。感觉xvid太牛了。编码效率极高,如今被x264代替了。。。扯远了,看看RGB2YUV的代码:

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

    ; *
    ; *	mmx colorspace conversions
    ; *
    ; *************************************************************************/
    
    bits 32
    
    
    section .data
    
    %macro cglobal 1 
    	%ifdef PREFIX
    		global _%1 
    		%define %1 _%1
    	%else
    		global %1
    	%endif
    %endmacro
    
    align 16
    
    
    ;===========================================================================
    ; yuv constants
    ;===========================================================================
    
    %define Y_R		0.257
    %define Y_G		0.504
    %define Y_B		0.098
    %define Y_ADD	16
    
    %define U_R		0.148
    %define U_G		0.291
    %define U_B		0.439
    %define U_ADD	128
    
    %define V_R		0.439
    %define V_G		0.368
    %define V_B		0.071
    %define V_ADD	128
    
    
    ;===========================================================================
    ; multiplication matrices
    ;===========================================================================
    
    ; %define SCALEBITS 8
    
    y_mul	dw		25		; FIX(Y_B)
    		dw		129		; FIX(Y_G)
    		dw		66		; FIX(Y_R)
    		dw		0
    
    u_mul	dw		112		; FIX(U_B)
    		dw		-74		; FIX(U_G)
    		dw		-38		; FIX(U_R)
    		dw		0
    
    v_mul	dw		-18		; FIX(V_B)
    		dw		-94		; FIX(V_G)
    		dw		112		; FIX(V_R)
    		dw		0
    
    
    
    section .text
    
    ;===========================================================================
    ;
    ;	void rgb24_to_yv12_mmx(uint8_t * const y_out,
    ;						uint8_t * const u_out,
    ;						uint8_t * const v_out,
    ;						const uint8_t * const src,
    ;						const uint32_t width,
    ;						const uint32_t height,
    ;						const uint32_t stride)
    ;
    ; always flips
    ;
    ;===========================================================================
    
    align 16
    cglobal rgb24_to_yv12_mmx
    rgb24_to_yv12_mmx
    
    		push ebx
    		push ecx
    		push esi
    		push edi			
    		push ebp			; STACK BASE = 20
    
    		; global consants
    
    		mov eax, [esp + 20 + 28]	; stride
    		mov ecx, [esp + 20 + 20]	; width
    		mov ebx, eax
    		sub ebx, ecx				
    		shr ebx, 1					; ebx = (stride-width) / 2;
    		push ebx					; [esp + 20] = uv_dif
    							; STACK BASE = 24
    
    		add eax, eax
    		sub eax, ecx				; eax = 2*stride - width
    		push eax					; [esp + 16] = y_dif
    							; STACK BASE = 28
    
    		mov ebx, ecx				; 
    		shr ebx, 1					;
    		push ebx					; [esp + 12] = width/2
    							; STACK BASE = 32
    
    		mov edx, ecx
    		add ecx, edx
    		add ecx, edx				; ecx = 3*width   (use 4 for rgb32)
    		push ecx					; [esp + 8] = width3
    							; STACK BASE = 36
    
    		mov edx, ecx
    		add edx, ecx
    		add edx, ecx				; edx = 3*width3
    		push edx					; [esp + 4] = src_dif
    							; STACK BASE = 40
    
    		mov esi, [esp + 40 + 16]	; src
    		mov ebp, [esp + 40 + 24]	; eax = height
    		mov eax, ebp
    		sub eax, 2
    		mul ecx
    		add esi, eax				; src += (height-2) * width3
    
    		mov edi, [esp + 40 + 4]		; y_out
    		mov ecx, [esp + 40 + 8]		; u_out
    		mov edx, [esp + 40 + 12]	; v_out
    		movq mm7, [y_mul]		
    
    		shr ebp, 1				; ebp = height / 2
    		push ebp				; [esp+0] = tmp
    								; STACK BASE = 44
    
    .yloop
    		mov ebp, [esp + 12]		; ebp = width /2 
    
    .xloop
    			; y_out
    
    			mov ebx, [esp + 8]			; ebx = width3
    
    			pxor mm4, mm4
    			pxor mm5, mm5
    			movd mm0, [esi]			; src[0...]
    			movd mm2, [esi+ebx]		; src[width3...]
    			punpcklbw mm0, mm4		; [  |b |g |r ]
    			punpcklbw mm2, mm5		; [  |b |g |r ]
    			movq mm6, mm0			; = [  |b4|g4|r4]
    			paddw mm6, mm2			; +[  |b4|g4|r4]
    			pmaddwd mm0, mm7		; *= Y_MUL
    			pmaddwd mm2, mm7		; *= Y_MUL
    			movq mm4, mm0			; [r]
    			movq mm5, mm2			; [r]
    			psrlq mm4, 32			; +[g]
    			psrlq mm5, 32			; +[g]
    			paddd mm0, mm4			; +[b]
    			paddd mm2, mm5			; +[b]
    
    			pxor mm4, mm4
    			pxor mm5, mm5
    			movd mm1, [esi+3]		; src[4...]
    			movd mm3, [esi+ebx+3]	; src[width3+4...]
    			punpcklbw mm1, mm4		; [  |b |g |r ]
    			punpcklbw mm3, mm5		; [  |b |g |r ]
    			paddw mm6, mm1			; +[  |b4|g4|r4]
    			paddw mm6, mm3			; +[  |b4|g4|r4]
    			pmaddwd mm1, mm7		; *= Y_MUL
    			pmaddwd mm3, mm7		; *= Y_MUL
    			movq mm4, mm1			; [r]
    			movq mm5, mm3			; [r]
    			psrlq mm4, 32			; +[g]
    			psrlq mm5, 32			; +[g]
    			paddd mm1, mm4			; +[b]
    			paddd mm3, mm5			; +[b]
    
    			mov ebx, [esp + 44 + 28]	; stride
    
    			movd eax, mm0
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi + ebx], al
    
    			movd eax, mm1
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi + ebx + 1], al
    
    			movd eax, mm2
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi], al
    
    			movd eax, mm3
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi + 1], al
    
    			; u_out, v_out
    
    			movq mm0, mm6			; = [  |b4|g4|r4]
    			pmaddwd mm6, [v_mul]		; *= V_MUL
    			pmaddwd mm0, [u_mul]		; *= U_MUL
    			movq mm1, mm0
    			movq mm2, mm6
    			psrlq mm1, 32
    			psrlq mm2, 32
    			paddd mm0, mm1
    			paddd mm2, mm6
    
    			movd eax, mm0
    			shr eax, 10
    			add eax, U_ADD
    			mov [ecx], al
    
    			movd eax, mm2
    			shr eax, 10
    			add eax, V_ADD
    			mov [edx], al
    
    			add esi, 2 * 3			; (use 4 for rgb32)
    			add edi, 2
    			inc ecx
    			inc edx
    
    			dec ebp
    			jnz near .xloop
    
    		sub esi, [esp + 4]			; src  -= src_dif
    		add edi, [esp + 16]			; y_out += y_dif
    		add ecx, [esp + 20]			; u_out += uv_dif
    		add edx, [esp + 20]			; v_out += uv_dif
    
    		dec dword [esp+0]
    		jnz near .yloop
    
    		emms
    
    		add esp, 24
    		pop ebp
    		pop edi
    		pop esi
    		pop ecx
    		pop ebx
    
    		ret
    
    
    
    ;===========================================================================
    ;
    ;	void rgb32_to_yv12mmx(uint8_t * const y_out,
    ;						uint8_t * const u_out,
    ;						uint8_t * const v_out,
    ;						const uint8_t * const src,
    ;						const uint32_t width,
    ;						const uint32_t height,
    ;						const uint32_t stride)
    ;
    ; always flips
    ;
    ;===========================================================================
    
    align 16
    cglobal rgb32_to_yv12_mmx
    rgb32_to_yv12_mmx
    
    		push ebx
    		push ecx
    		push esi
    		push edi			
    		push ebp			; STACK BASE = 20
    
    		; global consants
    
    		mov eax, [esp + 20 + 28]	; stride
    		mov ecx, [esp + 20 + 20]	; width
    		mov ebx, eax
    		sub ebx, ecx				
    		shr ebx, 1					; ebx = (stride-width) / 2;
    		push ebx					; [esp + 20] = uv_dif
    							; STACK BASE = 24
    
    		add eax, eax
    		sub eax, ecx				; eax = 2*stride - width
    		push eax					; [esp + 16] = y_dif
    							; STACK BASE = 28
    
    		mov ebx, ecx				; 
    		shr ebx, 1					;
    		push ebx					; [esp + 12] = width/2
    							; STACK BASE = 32
    
    		mov edx, ecx
    		shl ecx, 2					; ecx = 4*width   (use 4 for rgb32)
    		push ecx					; [esp + 8] = width4
    							; STACK BASE = 36
    
    		mov edx, ecx
    		add edx, ecx
    		add edx, ecx				; edx = 3*width4
    		push edx					; [esp + 4] = src_dif
    							; STACK BASE = 40
    
    		mov esi, [esp + 40 + 16]	; src
    		mov ebp, [esp + 40 + 24]	; eax = height
    		mov eax, ebp
    		sub eax, 2
    		mul ecx
    		add esi, eax				; src += (height-2) * width4
    
    		mov edi, [esp + 40 + 4]		; y_out
    		mov ecx, [esp + 40 + 8]		; u_out
    		mov edx, [esp + 40 + 12]	; v_out
    		movq mm7, [y_mul]		
    
    		shr ebp, 1				; ebp = height / 2
    		push ebp				; [esp+0] = tmp
    								; STACK BASE = 44
    
    .yloop
    		mov ebp, [esp + 12]		; ebp = width /2 
    
    .xloop
    			; y_out
    
    			mov ebx, [esp + 8]			; ebx = width4
    
    			pxor mm4, mm4
    			movq mm0, [esi]			; src[4...       |0...     ]
    			movq mm2, [esi+ebx]		; src[width4+4...|width4...]
    			movq mm1, mm0
    			movq mm3, mm2
    			punpcklbw mm0, mm4		; [  |b |g |r ]
    			punpcklbw mm2, mm4		; [  |b |g |r ]
    			punpckhbw mm1, mm4		; [  |b |g |r ]
    			punpckhbw mm3, mm4		; [  |b |g |r ]
    
    			movq mm6, mm0			; = [  |b4|g4|r4]
    			paddw mm6, mm2			; +[  |b4|g4|r4]
    			pmaddwd mm0, mm7		; *= Y_MUL
    			pmaddwd mm2, mm7		; *= Y_MUL
    			movq mm4, mm0			; [r]
    			movq mm5, mm2			; [r]
    			psrlq mm4, 32			; +[g]
    			psrlq mm5, 32			; +[g]
    			paddd mm0, mm4			; +[b]
    			paddd mm2, mm5			; +[b]
    
    			paddw mm6, mm1			; +[  |b4|g4|r4]
    			paddw mm6, mm3			; +[  |b4|g4|r4]
    			pmaddwd mm1, mm7		; *= Y_MUL
    			pmaddwd mm3, mm7		; *= Y_MUL
    			movq mm4, mm1			; [r]
    			movq mm5, mm3			; [r]
    			psrlq mm4, 32			; +[g]
    			psrlq mm5, 32			; +[g]
    			paddd mm1, mm4			; +[b]
    			paddd mm3, mm5			; +[b]
    
    			mov ebx, [esp + 44 + 28]	; stride
    
    			movd eax, mm0
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi + ebx], al
    
    			movd eax, mm1
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi + ebx + 1], al
    
    			movd eax, mm2
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi], al
    
    			movd eax, mm3
    			shr eax, 8
    			add eax, Y_ADD
    			mov [edi + 1], al
    
    			; u_out, v_out
    
    			movq mm0, mm6			; = [  |b4|g4|r4]
    			pmaddwd mm6, [v_mul]		; *= V_MUL
    			pmaddwd mm0, [u_mul]		; *= U_MUL
    			movq mm1, mm0
    			movq mm2, mm6
    			psrlq mm1, 32
    			psrlq mm2, 32
    			paddd mm0, mm1
    			paddd mm2, mm6
    
    			movd eax, mm0
    			shr eax, 10
    			add eax, U_ADD
    			mov [ecx], al
    
    			movd eax, mm2
    			shr eax, 10
    			add eax, V_ADD
    			mov [edx], al
    
    			add esi, 2 * 4			; (use 4 for rgb32)
    			add edi, 2
    			inc ecx
    			inc edx
    
    			dec ebp
    			jnz near .xloop
    
    		sub esi, [esp + 4]			; src  -= src_dif
    		add edi, [esp + 16]			; y_out += y_dif
    		add ecx, [esp + 20]			; u_out += uv_dif
    		add edx, [esp + 20]			; v_out += uv_dif
    
    		dec dword [esp+0]
    		jnz near .yloop
    
    		emms
    
    		add esp, 24
    		pop ebp
    		pop edi
    		pop esi
    		pop ecx
    		pop ebx
    
    		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文件中面。凝视掉几行代码

    //#ifdef _DEBUG
    //extern FILE *netstackdump;
    //extern FILE *netstackdump_read;
    //#endif
    //#ifdef _DEBUG
    //      fwrite(ptr, 1, nBytes, netstackdump_read);
    //#endif
    //#ifdef _DEBUG
    //  fwrite(buf, 1, len, netstackdump);
    //#endif

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

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

    /*分配与初始化*/
    rtmp = RTMP_Alloc();
    RTMP_Init(rtmp);
     
    /*设置URL*/
    if (RTMP_SetupURL(rtmp,rtmp_url) == FALSE) {
        log(LOG_ERR,"RTMP_SetupURL() failed!");
        RTMP_Free(rtmp);
        return -1;
    }
     
    /*设置可写,即公布流,这个函数必须在连接前使用,否则无效*/
    RTMP_EnableWrite(rtmp);
     
    /*连接server*/
    if (RTMP_Connect(rtmp, NULL) == FALSE) {
        log(LOG_ERR,"RTMP_Connect() failed!");
        RTMP_Free(rtmp);
        return -1;
    } 
     
    /*连接流*/
    if (RTMP_ConnectStream(rtmp,0) == FALSE) {
        log(LOG_ERR,"RTMP_ConnectStream() failed!");
        RTMP_Close(rtmp);
        RTMP_Free(rtmp);
        return -1;
    }

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

    size = x264_encoder_encode(cx->hd,&nal,&n,pic,&pout);
     
    int i,last;
    for (i = 0,last = 0;i < n;i++) {
        if (nal[i].i_type == NAL_SPS) {
     
            sps_len = nal[i].i_payload-4;
            memcpy(sps,nal[i].p_payload+4,sps_len);
     
        } else if (nal[i].i_type == NAL_PPS) {
     
            pps_len = nal[i].i_payload-4;
            memcpy(pps,nal[i].p_payload+4,pps_len);
     
            /*发送sps pps*/
            send_video_sps_pps();    
     
        } else {
     
            /*发送普通帧(剩下所有)*/
            send_rtmp_video(nal[i].p_payload,size-last)
            break;
        }
        last += nal[i].i_payload;
    }

        发送的代码就不贴了。就是填充rtmp packet的内容。调试的时候发现创建socket失败,折腾半天最后发现是没有调用WSAStartup,低级错误。。

    发送的数据流,用Flex air写了个接收程序。也有问题。air3.1能够正常接收。air3.9就不行。好吧。。。

    今天继续看代码吧,找找原因。音频的发送程序类似,有空再加。

        这个程序将会是我们即将公布的轻录播的一部分。这几天把其它代码完毕考虑把程序共享出来。

    欢迎拍砖。


    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    CCF-CSP201512-3 画图
    CCF-CSP201512-2 消除类游戏
    CCF-CSP201606-4 游戏(BFS)
    CCF-CSP201604-2 俄罗斯方块
    HDU1035 Robot Motion(dfs)
    Java Srting之Calendar日历类(五)——Calendar中计算时间的方法add()
    java如何获取当前日期和时间
    double 类型怎样不用科学计数法表示并且使用Java正则表达式去掉Double类型的数据后面多余的0
    @SpringBootApplication(exclude={DataSourceAutoConfiguration.class})注解作用
    java.util.Date.toString()方法实例
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4616513.html
Copyright © 2011-2022 走看看