博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
屏幕录制H.264视频,AAC音频,MP4复,LibRTMP现场活动
阅读量:5303 次
发布时间:2019-06-14

本文共 11484 字,大约阅读时间需要 38 分钟。

    上周完成了一个屏幕录制节目,实时屏幕捕获、记录,视频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 32section .data%macro cglobal 1 	%ifdef PREFIX		global _%1 		%define %1 _%1	%else		global %1	%endif%endmacroalign 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 8y_mul	dw		25		; FIX(Y_B)		dw		129		; FIX(Y_G)		dw		66		; FIX(Y_R)		dw		0u_mul	dw		112		; FIX(U_B)		dw		-74		; FIX(U_G)		dw		-38		; FIX(U_R)		dw		0v_mul	dw		-18		; FIX(V_B)		dw		-94		; FIX(V_G)		dw		112		; FIX(V_R)		dw		0section .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 16cglobal rgb24_to_yv12_mmxrgb24_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 16cglobal rgb32_to_yv12_mmxrgb32_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就不行。好吧。。。

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

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

欢迎拍砖。

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

转载于:https://www.cnblogs.com/mengfanrong/p/4616513.html

你可能感兴趣的文章
AT1983 BBQ Hard 解题报告
查看>>
AT2000 Leftmost Ball 解题报告
查看>>
JZOJPJ-C 8/21题解
查看>>
JZOJ823PJ-C, TG-B
查看>>
Empty
查看>>
LINUX - 寄存器和堆栈
查看>>
LINUX - mmap()
查看>>
LINUX - 随机数
查看>>
JAVA - 方法
查看>>
C - dlopen dlsym
查看>>
LINUX - socket
查看>>
LINUX - 最简单的CS通信实例
查看>>
GO - 高级编程
查看>>
LINUX - Libevent
查看>>
display:table实现多列等高布局
查看>>
css实现定宽高比(非内容撑出)
查看>>
少儿编程 PARACRAFT
查看>>
Redis 发布订阅
查看>>
Redis 事务
查看>>
中国创新教育交流会杂感
查看>>