前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android移动直播,自定义采集视频数据推流

Android移动直播,自定义采集视频数据推流

原创
作者头像
腾讯云-chaoli
修改2019-03-14 12:10:08
4.8K3
修改2019-03-14 12:10:08
举报

常见场景

目前腾讯视频云移动直播SDK(LiteAVSDK)只回调摄像机预览画面的纹理数据。如果开发者集成第三方美颜库来实现美颜、滤镜等功能,但第三方库的美颜功能输入数据要求是camera的原始数据(YUV 数据)。开发者想实现该功能,需要采用自定义采集视频数据接口,然后复用 LiteAVSDK 的编码和推流功能。

解决方案

Android5.0以上,通过camera2采集YUV_420_888

  1. 不再调用 TXLivePusherstartCameraPreview 接口。这样 SDK 本身就不会再采集视频数据和音频数据,而只是启动预处理、编码、流控、推流等工作。
  2. 在摄像机的预览回调onImageAvailable()中,获取到 YUV_420_888 格式的视频数据,然后将 YUV_420_888 格式转码为 I420 格式,再使用 sendCustomVideoData 向SDK填充您采集和处理后的 Video 数据。

具体实例代码如下:

ImageReader mImageReader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888 ,1);

ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {


        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        @Override
        public void onImageAvailable(ImageReader reader) {

            // 获取捕获的照片数据
            Image image = reader.acquireNextImage();
            int width2 = image.getWidth();
            int height2 = image.getHeight();
            if(isPushFlag){

                byte[] yuv420pbuf = camera2ImageToI420(image);

                //TODO setConfig()
                int result= mLivePusher.sendCustomVideoData(yuv420pbuf, TXLivePusher.YUV_420P, width2, height2);
            }
        }
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public byte[] camera2ImageToI420(Image image){
        int width3 = image.getWidth();
        int height3 = image.getHeight();

        // 从image里获取三个plane
        Image.Plane[] planes = image.getPlanes();
//        for (int i = 0; i < planes.length; i++) {
//           ByteBuffer iBuffer = planes[i].getBuffer();
//            int iSize = iBuffer.remaining();
//            Log.i("TAG", "pixelStride  " + planes[i].getPixelStride());
//            Log.i("TAG", "rowStride   " + planes[i].getRowStride());
//            Log.i("TAG", "width  " + image.getWidth());
//            Log.i("TAG", "height  " + image.getHeight());
//            Log.i("TAG", "buffer size  " + iSize);
//            Log.i("TAG", "Finished reading data from plane  " + i);
//        }
        // Y-buffer
        ByteBuffer yBuffer = planes[0].getBuffer();
        int ySize = yBuffer.remaining();
        byte[] yBytes = new byte[ySize];
        yBuffer.get(yBytes);

        // U-buffer
        ByteBuffer uBuffer = planes[1].getBuffer();
        int uSize = uBuffer.remaining();
        byte[] uBytes = new byte[uSize];
        uBuffer.get(uBytes);

        // V-buffer
        ByteBuffer vBuffer = planes[2].getBuffer();
        int vSize = vBuffer.remaining();
        byte[] vBytes = new byte[vSize];
        vBuffer.get(vBytes);

        byte[] ret = new byte[width3 * height3 * 3/2];
        int yLength = yBytes.length;
        int uLength = uBytes.length+1;
        int vLength = vBytes.length+1;

        ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, yLength);
        ByteBuffer bufferU = ByteBuffer.wrap(ret, yLength, uLength/ 2);
        ByteBuffer bufferV = ByteBuffer.wrap(ret, yLength+uLength/ 2, vLength/ 2);

        bufferY.put(yBytes,0,yLength);
        for(int i=0;i<uLength;i+=2){
            bufferU.put(uBytes[i]);
        }
        for(int i=0;i<vLength;i+=2){
            bufferV.put(vBytes[i]);
        }

        return ret;
    }

Android5.0以下,通过camera采集到NV21数据

  1. 不再调用 TXLivePusherstartCameraPreview 接口。这样 SDK 本身就不会再采集视频数据和音频数据,而只是启动预处理、编码、流控、推流等工作。
  2. 在摄像机的预览回调onPreviewFrame()中,获取到 NV21 格式的视频数据,然后将 NV21 格式转码为 I420 格式,再使用 sendCustomVideoData 向SDK填充您采集和处理后的 Video 数据。

具体实例代码如下:

//以下是简单的实例,获取摄像机预览回调的视频数据并推流
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
    // 假设摄像机获取的视频格式是 NV21, 预览画面大小为 1280X720
    // 即宽度 mPreviewWidth 值为1280,高度 mPreviewHeight 值为 720
    if (!isPush) {
    } else {
        // 开始自定义推流
        // 需要将视频格式转码为 I420
        byte[] buffer = new byte[data.length];
        buffer = nv21ToI420(data, mPreviewWidth, mPreviewHeight);

        int customModeType = 0;
        customModeType |= TXLiveConstants.CUSTOM_MODE_VIDEO_CAPTURE;
        // 只能分辨率的宽和高小于或者等于预览画面的宽和高的分辨率 
        // 还能选择 360x640 等,但不能选择 540x960。因指定分辨率的高(960) > 预览画面的高(720),编码器无法裁剪画面。
        mLivePushConfig.setVideoResolution(TXLiveConstants.VIDEO_RESOLUTION_TYPE_1280_720);
        mLivePushConfig.setAutoAdjustBitrate(false);
        mLivePushConfig.setVideoBitrate(1500);
        mLivePushConfig.setVideoEncodeGop(3);
        mLivePushConfig.setVideoFPS(18);
        mLivePushConfig.setCustomModeType(customModeType);
        mLivePusher.setConfig(mLivePushConfig);

        int result= mLivePusher.sendCustomVideoData(buffer, TXLivePusher.YUV_420P, mPreviewWidth, mPreviewHeight);
    }
}

/**
 * nv21转I420
 * @param data
 * @param width
 * @param height
 * @return
 */
public static byte[] nv21ToI420(byte[] data, int width, int height) {
    byte[] ret = new byte[data.length];
    int total = width * height;

    ByteBuffer bufferY = ByteBuffer.wrap(ret, 0, total);
    ByteBuffer bufferU = ByteBuffer.wrap(ret, total, total / 4);
    ByteBuffer bufferV = ByteBuffer.wrap(ret, total + total / 4, total / 4);

    bufferY.put(data, 0, total);
    for (int i=total; i<data.length; i+=2) {
        bufferV.put(data[i]);
        bufferU.put(data[i+1]);
    }
    return ret;
}

camera2完整的示例代码下载地址, 建议将代码复制到腾讯云开发者demo中

camera完整的示例代码下载地址, 建议将代码复制到腾讯云开发者demo中

原理

接口说明

int sendCustomVideoData(byte[] buffer, int bufferType, int w, int h)

该接口是向 SDK 传入开发者自定义采集和处理后的视频数据(美颜、滤镜等),目前支持 I420 格式。该接口适用场景是只想使用我们 SDK 来 来编码和推流。 调用该接口前提,是不再调用 TXLivePusherstartCameraPreview 接口。

  • 参数说明

参数

类型

说明

buffer

byte[]

视频数据

bufferType

int

视频格式.目前只支持 TXLivePusher.YUV_420P

w

int

视频图像的宽度

h

int

视频图像的高度

  • 返回结果说明:

结果

说明

>0

发送成功,但帧率过高,超过了TXLivePushConfig中设置的帧率,帧率过高会导致视频编码器输出的码率超过TXLivePushConfig中设置的码率,返回值表示当前YUV视频帧提前的毫秒数

0

发送成功

-1

视频分辨率非法

-2

YUV数据长度与设置的视频分辨率所要求的长度不一致

-3

视频格式非法

-4

视频图像长宽不符合要求,画面比要求的小了

-1000

SDK内部错误

自定义采集数据流程图

注意事项

  1. 目前sendCustomVideoData接口只支持 I420(TXLivePusher.YUV_420P)格式的视频数据。
  2. sendCustomVideoData 方法最后两个参数是摄像机预览画面的宽度和高度,必需保持一致,不然会报出 -4 的错误。camera2在获取摄像机预览宽高前,请先检测手机支持的分辨率,如果指定分辨率与支持的分辨率不一致,会获取到比指定分辨率小的画面,sendCustomVideoData时要以实际预览画面的宽高为准。
  3. 指定推流分辨率(setVideoResolution)的宽度(高度)一定要小于或者等于摄像机预览画面的宽度(高度)。例如预览分辨率是960x720,设置推流的分辨率可以 960x540。
  4. LivePushConfig 中的customModeType 设置为TXLiveConstants.CUSTOM_MODE_VIDEO_CAPTURE,SDK 还是会采集音频数据的。
  5. 使用LivePushConfig.setVideoResolution设置推流分辨率,目前 sendCustomVideoData 只支持推 640x360(360P)、360x640、960x540(540P)、540x960、1280x720(720P)、720x1280这6种分辨率

iOS移动直播,自定义采集视频数据推流

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 常见场景
  • 解决方案
    • Android5.0以上,通过camera2采集YUV_420_888
      • Android5.0以下,通过camera采集到NV21数据
      • 原理
        • 接口说明
          • 自定义采集数据流程图
          • 注意事项
          相关产品与服务
          云直播
          云直播(Cloud Streaming Services,CSS)为您提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,云直播提供了标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,为您提供一站式的音视频直播解决方案。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档