屏幕录制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就不行。好吧。。。
今天继续看代码吧,找找原因。音频的发送程序类似,有空再加。
这个程序将会是我们即将公布的轻录播的一部分。这几天把其它代码完毕考虑把程序共享出来。
欢迎拍砖。
版权声明:本文博客原创文章。博客,未经同意,不得转载。
屏幕录制H.264视频,AAC音频,MP4复,LibRTMP现场活动的更多相关文章
- 视音频数据处理入门:H.264视频码流解析
===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB.YUV像素数据处理 视音频数据处理 ...
- H.264 RTPpayload 格式------ H.264 视频 RTP 负载格式(包含AAC部分解析)
H.264 RTPpayload 格式------ H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) NALU 头由一个字节组成, 它的语法如下: +------------ ...
- 工具---《.264视频 转成 MP4视频》
<.264视频 转成 MP4视频> 安装了“爱奇艺万能播放器”可以打开.264视频,但是opencv却不能直接读取.264视频,还是需要想办法“.264视频 转成 MP4/avi视频”. ...
- 【转】实现RTP协议的H.264视频传输系统
1. 引言 随着信息产业的发展,人们对信息资源的要求已经逐渐由文字和图片过渡到音频和视频,并越来越强调获取资源的实时性和互动性.但人们又面临着另外一种不可避免的尴尬,就是在网络上看到生动 ...
- H.264视频的RTP荷载格式
Status of This Memo This document specifies an Internet standards track protocol for the Internet ...
- H.264视频在android手机端的解码与播放(转)
随着无线网络和智能手机的发展,智能手机与人们日常生活联系越来越紧密,娱乐.商务应用.金融应用.交通出行各种功能的软件大批涌现,使得人们的生活丰富多彩.快捷便利,也让它成为人们生活中不可取代的一部分.其 ...
- H.264 RTPpayload 格式------ H.264 视频 RTP 负载格式
H.264 RTPpayload 格式------ H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) NALU 头由一个字节组成, 它的语法如下: +------------ ...
- H.264视频编解码SoC满足高清DVR设计需求
硬盘录像机(DVR)作为监控系统的核心部件之一,在10年里高速发展,从模拟磁带机的替代品演变成具有自己独特价值的专业监控数字平台,并被市场广泛接受.监控系统伴随DVR这些年的发展向着IP化.智能化发展 ...
- ASP.NET MVC程序播放H.264视频
在这篇之前,Insus.NET不管是在ASP.NET还是ASP.NET MVC实现很多视频播放,你可以参考这篇链接:http://www.cnblogs.com/insus/category/4650 ...
随机推荐
- DDD领域驱动设计仓储Repository
DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...
- Shell编程入门(再版)(在)
简单的演示样本Shell规划 演示样例1. #!/bin/bash #This is to show what a shell script looks like echo "Our fir ...
- Android使用HttpClient方法和易错问题
HttpClient为Android开发人员提供了跟简洁的操作Http网络连接的方法,在连接过程中也有两种方式,get和post,先看一下怎样实现的 默认是get方式 //先将參数放入List,再对參 ...
- 使 IIS 6.0 可以在 64 位 Windows 上运行 32 位应用程序 试图加载格式不正确的程序。
原文 使 IIS 6.0 可以在 64 位 Windows 上运行 32 位应用程序 试图加载格式不正确的程序. win7 64位操作系统上边运行IIS网站应用的时候,提示错误"试图加载格式 ...
- Java虚拟机类型卸载和类型更新解析(转)
转自:http://www.blogjava.net/zhuxing/archive/2008/07/24/217285.html [摘要] 前面系统讨论过java 类型加载(loa ...
- javaEE异常摘要——更换工作区相同tomcat当部署在同一个项目疑难解答
我有一个项目,我的工作区公告,没问题,它可以运行正常,但我把项目copy还有一个工作空间,然后发布到tomcat(随着tomcat,先前的工作空间remove deployment,公布信息)上去,想 ...
- Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Multiple representations of the same entity解决方法
1.错误信息 Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUs ...
- CocoaPods停在Analyzing dependencies解决方案
现在很多开源项目应用cocoapod.这使集成第三方库都非常方便,在没有花project里设置哪些参数.仗着. 只要运行pod update要么pod install时间,经常会卡在Analyzing ...
- 假设动态运行java文字,当在脚本式配置,这是非常方便的
package com.bfrj.core.groovy; import java.util.HashMap; import java.util.Map; import org.jeecgframew ...
- 小记 js unicode 编码解析
原文:小记 js unicode 编码解析 var str = "\\u6211\\u662Funicode\\u7F16\\u7801"; 关于这样的数据转换为中文问题,常用的两 ...