目前腾讯视频云移动直播SDK(LiteAVSDK)只回调摄像机预览画面的纹理数据。如果开发者集成第三方美颜库来实现美颜、滤镜等功能,但第三方库的美颜功能输入数据要求是camera的原始数据(YUV 数据)。开发者想实现该功能,需要采用自定义采集视频数据接口,然后复用 LiteAVSDK 的编码和推流功能。
TXLivePusher
的 startCameraPreview
接口。这样 SDK 本身就不会再采集视频数据和音频数据,而只是启动预处理、编码、流控、推流等工作。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;
}
TXLivePusher
的 startCameraPreview
接口。这样 SDK 本身就不会再采集视频数据和音频数据,而只是启动预处理、编码、流控、推流等工作。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 来 来编码和推流。 调用该接口前提,是不再调用 TXLivePusher
的 startCameraPreview
接口。
参数 | 类型 | 说明 |
---|---|---|
buffer | byte[] | 视频数据 |
bufferType | int | 视频格式.目前只支持 TXLivePusher.YUV_420P |
w | int | 视频图像的宽度 |
h | int | 视频图像的高度 |
结果 | 说明 |
---|---|
>0 | 发送成功,但帧率过高,超过了TXLivePushConfig中设置的帧率,帧率过高会导致视频编码器输出的码率超过TXLivePushConfig中设置的码率,返回值表示当前YUV视频帧提前的毫秒数 |
0 | 发送成功 |
-1 | 视频分辨率非法 |
-2 | YUV数据长度与设置的视频分辨率所要求的长度不一致 |
-3 | 视频格式非法 |
-4 | 视频图像长宽不符合要求,画面比要求的小了 |
-1000 | SDK内部错误 |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。