本文为您介绍了Android的输入外部视频流和音频流的接口示例。
输入外部视频流
注意 增加自定义视频输入时,建议您提前采集,RTC进入频道之前开始送入数据,避免看到摄像头数据。
- 注册外部视频接口回调。
接口说明如下所示。
/** * 注册原始数据回调接口 * @param streamType 流类型 * @return dataInterface 视频原始数据接口 */ public abstract AliRtcEngine.VideoRawDataInterface registerVideoRawDataInterface(AliRtcEngine.AliRawDataStreamType streamType);
说明 streamType支持AliRTCSdk_Streame_Type_Capture(视频流)和AliRTCSdk_Streame_Type_Screen(屏幕流);SDK默认自动推拉流,例如:主动关闭将收不到对应回调。示例方法如下所示。
//获取AliRtcEngine实例 AliRtcEngine mAliRtcEngine = AliRtcEngine.getInstance(getApplicationContext()); AliRtcEngine.VideoRawDataInterface videoRawDataInterface = mAliRtcEngine.registerVideoRawDataInterface( AliRtcEngine.AliRawDataStreamType.AliRTCSdk_Streame_Type_Capture);
- 输入视频数据。
接口说明如下所示。
/** * 发送视频帧 * @param frame AliRawDataFrame数据帧实体类 * @param timeStamp 时间戳 * @return */ public abstract int deliverFrame(AliRawDataFrame frame, long timeStamp);
参数说明,AliRawDataFrame必填项如下所示。
参数 说明 MediaStatesVideoFormat 数据类型,目前默认AndroidYUV数据返回NV21 width 视频流宽 height 视频流高 rotation 视频流角度 video_frame_length 数据buffer长度 lineSize yuv数据数组,具体使用请参见下文示例 frame buffer数据 示例方法如下所示。
private void decodeYUVRawData(AliRtcEngine.VideoRawDataInterface videoRawDataInterface) { String yuvPath = "<!-请输入yuv文件地址->"; if (TextUtils.isEmpty(yuvPath)) { ToastUtils.LongToast("请先选择YUV文件!!!"); return; } File yuvDataFile = new File(yuvPath); if (!yuvDataFile.exists()) { ToastUtils.LongToast(yuvPath + " 文件不存在!!!"); return; } //此处仅为示例代码做法,请使用正确的流角度 int rotation = 90; Log.d(TAG, "decodeYUVRawData: "+rotation+"__"+getVideoFormat()); ToastUtils.LongToast("inputing yuv data"); //此处仅为示例代码做法,请使用正确的流宽高 int width = 480; int height = 640; //start push yuv new Thread() { @Override public void run() { File yuvDataFile = new File(yuvPath); RandomAccessFile raf = null; try { raf = new RandomAccessFile(yuvDataFile, "r"); } catch (FileNotFoundException e) { e.printStackTrace(); return; } try { byte[] buffer = new byte[width * height * 3 / 2]; while (videoRawDataInterface != null) { int len = raf.read(buffer); if (len == -1) { raf.seek(0); } AliRtcEngine.AliRawDataFrame rawDataFrame = new AliRtcEngine.AliRawDataFrame(); rawDataFrame.format = MediaStatesVideoFormat.NV21;// 支持NV21和I420 rawDataFrame.frame = buffer; rawDataFrame.width = width; rawDataFrame.height = height; rawDataFrame.lineSize[0] = width; rawDataFrame.lineSize[1] = width / 2; rawDataFrame.lineSize[2] = height / 2; rawDataFrame.lineSize[3] = 0; rawDataFrame.rotation = rotation; rawDataFrame.video_frame_length = buffer.length; if (videoRawDataInterface != null) { videoRawDataInterface.deliverFrame(rawDataFrame, System.nanoTime() / 1000); } //主动sleep发送一次 Thread.sleep(25); } } catch (IOException | InterruptedException ex) { ex.printStackTrace(); } finally { try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); }
- 反注册视频数据回调。
接口方法如下所示。
/** * 反注册原始数据回调接口 * @param streamType 流类型 */ public abstract void unRegisterVideoRawDataInterface(AliRtcEngine.AliRawDataStreamType streamType);
示例方法如下所示。
mAliRtcEngine.unRegisterVideoRawDataInterface( type);
输入外部音频流
- 打开外部音频输入开关。
接口如下所示。
/** * @brief 设置是否启用外部音频输入源 * @param enable YES:开启,NO:关闭 * @param sampleRate 采样率,16k、48k... * @param channels 外部音频源的通道数,可设置为1:单声道,2:双声道 * @return 返回>=0表示成功, <0表示失败 */ public abstract int setExternalAudioSource(boolean enable ,int sampleRate,int channels); /** * @brief 设置是否与麦克风采集音频混合 * @param mixed YES:混音,NO:完全替换麦克风采集数据 */ public abstract int setMixedWithMic(boolean mixed);
说明 目前仅支持PCM数据。示例方法如下所示。
//获取mAliRtcEngine, AliRtcEngine mAliRtcEngine = AliRtcEngine.getInstance(getApplicationContext()); //设置开启外部音频输入源 mAliRtcEngine.setExternalAudioSource(true,44100,1); //完全替代麦克风采集 mAliRtcEngine.setMixedWithMic(false);
- 输入音频数据。
接口方法如下所示。
/** * 输入外部音频数据 * @param samples 音频数据 不建议超过40ms数据 * @param samplesLength 采样长度 * @param timestamp 时间戳 * @return return>=0 Success, return<0 Failure */ public abstract int pushExternalAudioFrameRawData(byte[] samples,int samplesLength,long timestamp);
说明 如果返回值为17236225,代表当前buffer队列塞满,需要等待后再继续输送数据,建议您等待20ms。示例方法如下所示。
private void decodePCMRawData(){ String pcmPath = "/sdcard/123.pcm"; if (TextUtils.isEmpty(pcmPath)) { ToastUtils.LongToast("请先选择PCM文件!!!"); return; } File pcmDataFile = new File(pcmPath); if (!pcmDataFile.exists()) { ToastUtils.LongToast(pcmPath + " 文件不存在!!!"); return; } ToastUtils.LongToast("inputing pcm data"); new Thread() { @Override public void run() { File pcmDataFile = new File(pcmPath); RandomAccessFile raf = null; try { raf = new RandomAccessFile(pcmDataFile, "r"); } catch (FileNotFoundException e) { e.printStackTrace(); return; } try { byte[] buffer = new byte[(44100/100)*4*1]; while (true) { int len = raf.read(buffer); if (len == -1) { raf.seek(0); } mAliRtcEngine.pushExternalAudioFrameRawData(buffer,buffer.length,0); Thread.sleep(20); } } catch (IOException | InterruptedException ex) { ex.printStackTrace(); } finally { try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); }
在文档使用中是否遇到以下问题
更多建议
匿名提交