android java延时函数_Android实时直播,一千行java搞定不依赖jni,延迟0.8至3秒,强悍...

在Android高版本中,特别是4.1引入了MediaCodec可以对摄像头的图像进行硬件编码,实现直播。一般Android推流到服务器,使用ffmpeg居多,也就是软编码,实际上使

项目首页:https://github.com/simple-rtmp-server/android-publisher

在Android高版本中,特别是4.1引入了MediaCodec可以对摄像头的图像进行硬件编码,实现直播。

一般Android推流到服务器,使用ffmpeg居多,也就是软编码,实际上使用Android的硬件编码会有更好的体验。

看了下网上的文章也不少,但是都缺乏一个整体跑通的方案,特别是如何推送的服务器。本文把Android推直播流的过程梳理一遍。

AndroidPublisher提出了Android直播的新思路,主要配合SRS服务器完成,优势如下:

Android直播有几个大的环节:

打开Camera,进行Preview获取YUV图像数据,也就是未压缩的图像。

设置picture和preview大小后,计算YUV的buffer的尺寸,不能简单乘以1.5而应该按照文档计算。

获取YUV的同时,还可以进行预览,只要绑定到SurfaceHolder就可以。

使用MediaCodec和MediaFormat对YUV进行编码,其中MediaCodec是编码,MediaFormat是打包成annexb封装。

设置MediaCodec的colorFormat需要判断是否MediaCodec支持,也就是从MediaCodec获取colorFormat。

将YUV图像,送入MediaCodec的inputBuffer,并获取outputBuffer中已经编码的数据,格式是annexb。

其中queueInputBuffer时,需要指定pts,否则没有编码数据输出,会被丢弃。

将编码的annexb数据,发送到服务器。

一般使用rtmp(librtmp/srslibrtmp/ffmpeg),因为流媒体服务器的输入一般是rtmp。

若服务器支持http-flv流POST,那么可以直接发送给服务器。

下面是各个重要环节的分解。

YUV图像

第一个环节,打开Camera并预览:

camera = Camera.open();

Camera.Parameters parameters = camera.getParameters();

parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);

parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);

parameters.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);

parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

parameters.setPreviewFormat(ImageFormat.YV12);

Camera.Size size = null;

List sizes = parameters.getSupportedPictureSizes();

for (int i = 0; i < sizes.size(); i++) {

//Log.i(TAG, String.format("camera supported picture size %dx%d", sizes.get(i).width, sizes.get(i).height));

if (sizes.get(i).width == 640) {

size = sizes.get(i);

}

}

parameters.setPictureSize(size.width, size.height);

Log.i(TAG, String.format("set the picture size in %dx%d", size.width, size.height));

sizes = parameters.getSupportedPreviewSizes();

for (int i = 0; i < sizes.size(); i++) {

//Log.i(TAG, String.format("camera supported preview size %dx%d", sizes.get(i).width, sizes.get(i).height));

if (sizes.get(i).width == 640) {

vsize = size = sizes.get(i);

}

}

parameters.setPreviewSize(size.width, size.height);

Log.i(TAG, String.format("set the preview size in %dx%d", size.width, size.height));

camera.setParameters(parameters);

// set the callback and start the preview.

buffer = new byte[getYuvBuffer(size.width, size.height)];

camera.addCallbackBuffer(buffer);

camera.setPreviewCallbackWithBuffer(onYuvFrame);

try {

camera.setPreviewDisplay(preview.getHolder());

} catch (IOException e) {

Log.e(TAG, "preview video failed.");

e.printStackTrace();

return;

}

Log.i(TAG, String.format("start to preview video in %dx%d, buffer %dB", size.width, size.height, buffer.length));

camera.startPreview();

计算YUV的buffer的函数,需要根据文档计算,而不是简单“*3/2”:

// for the buffer for YV12(android YUV), @see below:

// https://developer.android.com/reference/android/hardware/Camera.Parameters.html#setPreviewFormat(int)

// https://developer.android.com/reference/android/graphics/ImageFormat.html#YV12

private int getYuvBuffer(int width, int height) {

// stride = ALIGN(width, 16)

int stride = (int)Math.ceil(width / 16.0) * 16;

// y_size = stride * height

int y_size = stride * height;

// c_stride = ALIGN(stride/2, 16)

int c_stride = (int)Math.ceil(width / 32.0) * 16;

// c_size = c_stride * height/2

int c_size = c_stride * height / 2;

// size = y_size + c_size * 2

return y_size + c_size * 2;

}

图像编码

第二个环节,设置编码器参数,并启动:

// encoder yuv to 264 es stream.

// requires sdk level 16+, Android 4.1, 4.1.1, the JELLY_BEAN

try {

encoder = MediaCodec.createEncoderByType(VCODEC);

} catch (IOException e) {

Log.e(TAG, "create encoder failed.");

e.printStackTrace();

return;

}

ebi = new MediaCodec.BufferInfo();

presentationTimeUs = new Date().getTime() * 1000;

// start the encoder.

// @see https://developer.android.com/reference/android/media/MediaCodec.html

MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, vsize.width, vsize.height);

format.setInteger(MediaFormat.KEY_BIT_RATE, 125000);

format.setInteger(MediaFormat.KEY_FRAME_RATE, 15);

format.setInteger(MediaFormat.KEY_COLOR_FORMAT, chooseColorFormat());

format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);

encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

encoder.start();

Log.i(TAG, "encoder start");

其中,colorFormat需要从编码器支持的格式中选取,否则会有不支持的错误:

// choose the right supported color format. @see below:

// https://developer.android.com/reference/android/media/MediaCodecInfo.html

// https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities.html

private int chooseColorFormat() {

MediaCodecInfo ci = null;

int nbCodecs = MediaCodecList.getCodecCount();

for (int i = 0; i < nbCodecs; i++) {

MediaCodecInfo mci = MediaCodecList.getCodecInfoAt(i);

if (!mci.isEncoder()) {

continue;

}

String[] types = mci.getSupportedTypes();

for (int j = 0; j < types.length; j++) {

if (types[j].equalsIgnoreCase(VCODEC)) {

//Log.i(TAG, String.format("encoder %s types: %s", mci.getName(), types[j]));

ci = mci;

break;

}

}

}

int matchedColorFormat = 0;

MediaCodecInfo.CodecCapabilities cc = ci.getCapabilitiesForType(VCODEC);

for (int i = 0; i < cc.colorFormats.length; i++) {

int cf = cc.colorFormats[i];

//Log.i(TAG, String.format("encoder %s supports color fomart %d", ci.getName(), cf));

// choose YUV for h.264, prefer the bigger one.

if (cf >= cc.COLOR_FormatYUV411Planar && cf <= cc.COLOR_FormatYUV422SemiPlanar) {

if (cf > matchedColorFormat) {

matchedColorFormat = cf;

}

}

}

Log.i(TAG, String.format("encoder %s choose color format %d", ci.getName(), matchedColorFormat));

return matchedColorFormat;

}

第三个环节,在YUV图像回调中,送给编码器,并获取输出:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值