公司最近有和云游戏相关的业务,最开始采用的是virtualdisplay +mediacodec实现,屏幕视频录制编码推流。但是mediacodec编码有很多参数设置不了,而且云主机的cpu性能完完全全高于GPU 所以,就准备采用软件编码实现。基于X264+minicap实现也可以理解为把bitmap转为H264视频通过RTMP传输。
先看效果图:
云手机同步效果
先上流程图:
1 minicap :是一个高速的截图工具,具体如何安装使用可以查看github上的流程
2 数据解析:minicap提供了一个localsocket往外吐数据我们可以在Android端解析该数据,关键代码如下
private void getMinicapData() {
new Thread(new Runnable() {
@Override
public void run() {
try {
mMinicapClientSocket.connect(mAddr);
InputStream inputStream = mMinicapClientSocket.getInputStream();
long start = System.currentTimeMillis();
while (isLiving) {
byte[] buffer = new byte[FRAME_SIZE];
int realLen = inputStream.read(buffer);
if (buffer.length != realLen) {
buffer = subByteArray(buffer, 0, realLen);
}
int len = buffer.length;
for (int cursor = 0; cursor < len; ) {
int byte10 = buffer[cursor] & 0xff;
if (readBannerBytes < bannerLength) {
cursor = parserBanner(cursor, byte10);
} else if (readFrameBytes < 4) {
// 第二次的缓冲区中前4位数字和为frame的缓冲区大小
frameBodyLength += (byte10 << (readFrameBytes * 8)) >>> 0;
cursor += 1;
readFrameBytes += 1;
Log.i(TAG, "解析图片大小 = " + readFrameBytes);
} else {
if (len - cursor >= frameBodyLength) {
Log.i(TAG, "frameBodyLength = " + frameBodyLength);
byte[] subByte = subByteArray(buffer, cursor,
cursor + frameBodyLength);
frameBody = byteMerger(frameBody, subByte);
if ((frameBody[0] != -1) || frameBody[1] != -40) {
Log.i(TAG, String.format("Frame body does not start with JPG header"));
return;
}
final byte[] finalBytes = subByteArray(frameBody, 0, frameBody.length);
// 转化成bitmap
mBitmap = BitmapFactory.decodeByteArray(finalBytes, 0, finalBytes.length);
// 这里开始推送
mLivePusher.native_push_video(miniCapRGBChange.getYUVByBitmap(mBitmap));
long current = System.currentTimeMillis();
Log.i(TAG, "图片已生成,耗时: "
+ TimeUtil.formatElapsedTime(current
- start));
start = current;
cursor += frameBodyLength;
restore();
} else {
Log.i(TAG, "所需数据大小 : " + frameBodyLength);
byte[] subByte = subByteArray(buffer, cursor, len);
frameBody = byteMerger(frameBody, subByte);
frameBodyLength -= (len - cursor);
readFrameBytes += (len - cursor);
cursor = len;
}
}
}
}
} catch (Exception e) {
Log.i(TAG, String.format(" get mini data Exception"+e));
}
}
}).start();
}
3 .android 移植x264
x264是一个免费的开源库,可以移植到Android上来,具体可查看官网或者网上搜索如何移植。
编码参数关键代码
void VideoChannel::setVideoEncodeInfo(int width, int height, int fps, int bitrate) {
// 编码参数设置可以参考
//https://www.cnblogs.com/wainiwann/p/5647521.html
pthread_mutex_lock(&mutex);
mWidth = width;
mHeight = height;
mFps = fps;
mBitrate = bitrate;
ySize = width * height;
uvSize = ySize / 4;
if (videoCodec) {
x264_encoder_close(videoCodec);
videoCodec = 0;
}
if (pic_in) {
x264_picture_clean(pic_in);
delete pic_in;
pic_in = 0;
}
//打开x264解码器
//x264解码器的属性
x264_param_t param;
//ultrafast 最快
//zerolatency 无延迟解码
x264_param_default_preset(¶m, "ultrafast", "zerolatency");
param.i_level_idc = 30;
//输入数据格式 int csp=X264_CSP_BGR|X264_CSP_VFLIP; //这个格式是BITMAP的那种颠倒的BGR的格式
param.i_csp = X264_CSP_I420;
param.i_width = width;
param.i_height = height;
//无b帧
param.i_bframe = 0;
//参数i_rc_method表示码率控制, CQP(恒定质量) CRF(恒定码率) ABR(平均码率)
param.rc.i_rc_method = X264_RC_CRF;
//码率(比特率 单位Kbps)
param.rc.i_bitrate = bitrate / 1000;
LOGI("set_i_bitrate------------------>%d",bitrate/1000);
//瞬时最大码率
param.rc.i_vbv_max_bitrate = bitrate / 1000 * 1.2;
//设置了i_vbv_max_bitrate必须设置此参数, 码率控制区大小 单位kbps
param.rc.i_vbv_buffer_size = bitrate / 1000;
//帧率
param.i_fps_num = fps;
param.i_fps_den = 1;
param.i_timebase_den = param.i_fps_num;
param.i_timebase_num = param.i_fps_den;
//用fps而不是时间戳来计算帧间距离
param.b_vfr_input = 0;
//帧距离(关键帧) 2s一个关键帧
param.i_keyint_max = fps * 2;
//是否赋值sps和pps放在每个关键帧的前面 该参数设置是让每个关键帧(I帧)都附带sps/pps
param.b_repeat_headers = 1;
//多线程
param.i_threads = 1;
x264_param_apply_profile(¶m, "baseline");
// x264_param_apply_profile(¶m, "high");
//打开解码器
videoCodec = x264_encoder_open(¶m);
pic_in = new x264_picture_t;
x264_picture_alloc(pic_in, X264_CSP_I420, width, height);
pthread_mutex_unlock(&mutex);
}
4 rtmp移植 :网上教程比较多可自行参考。
5最后效果图:
时间来不及就先看看图片吧。
老版本的屏幕录制可以看看上一篇文章,新版的屏幕录制需要搭好minicap环境,有些9.0手机可能不能允许minicap,如果是手机的话还是建议采用上一篇文章的方案,如果是自己要做这方面的业务可以采用新版本。 新版本的可以等我后面放到github上。
优化后在Android 和网页上的效果如下: