全部产品
云市场

视频合拍

更新时间:2019-07-01 11:42:06

功能介绍

短视频SDK提供视频合拍功能AliyunIMixRecorder,即使用一个已有视频作为样本视频,与摄像头采集的数据按照特定的布局方式(比如左右分屏,上下分屏,画中画等)进行合拍录制。

概念介绍

在以下文档介绍中将提及一些特殊概念,为方便开发者理解,预先对概念做一些讲解。

1、合拍功能

视频合拍从产品功能层面看,就是两路视频(一路来自样本视频,一路来自设备摄像头采集),按照指定的布局模式(左右分屏,上下分屏,画中画等)进行合成,合成出来的视频每一帧画面将会同时包含两路视频的画面,而合拍视频的音频部分则采用样本视频的音频。以下为范例视图,实际上SDK内部支持开发者自己组织布局,关于如何布局将在后面讲述。

track

2、轨道

在概念1中提及的两路视频在SDK中被抽象为两个轨道:A轨道和B轨道,A轨放设备采集的视频,B轨放样本视频,用轨道抽象有利于开发者理解轨道布局的概念。

3、轨道布局

轨道布局是轨道的属性之一,用来描述,该轨道的视频画面,在合拍生成的视频中如何“摆放”,轨道布局在一个归一化的坐标系中,从两个纬度来描述轨道布局信息,分别是中心点的坐标和轨道size(即宽高信息)。

如下图所示,在该布局画面中,轨道A和轨道B的画面各占一半,因此,两个轨道的宽度均为0.5,而高度则都为1.0,而轨道A的中心点坐标为(0.25,0.5),轨道B的中心点坐标为(0.75,0.5)。

轨道布局在合拍接口中主要涉及AliyunMixRecorderDisplayParam和AliyunMixTrackLayoutParam两个核心类,其中AliyunMixTrackLayoutParam就是用来描述上述提到的中心点和宽高信息的,而AliyunMixRecorderDisplayParam里面除了包含AliyunMixTrackLayoutParam布局信息,还包含了displayMode和layoutLevel,displayMode描述了该轨道的视频在宽高比与轨道宽高不一致时采用填充还是裁剪模式,layoutLevel用来描述布局层级,假如两个轨道有相互覆盖的地方,则layoutLevel大的轨道,在布局上层,将覆盖layoutLevel小的轨道,具体可以参考相应的接口文档。

layout

版本差异

版本 功能描述
专业版 支持合拍功能
标准版 支持合拍功能
基础版 不支持合拍功能

合拍录制流程

合拍录制功能需要获取摄像头和麦克风权限,否则无法录制。视频合拍录制的流程如下:

1. 配置参数
2. 回调设置
3. 开启预览
4. 录制控制/管理
5. 设置特效
6. 开始录制
7. 完成录制

配置参数

初始化

  • 创建合拍接口实例
    1. AliyunIMixRecorder recorder = AliyunMixRecorderCreator.createAlivcMixRecorderInstance(Context context);//参数context建议使用Application Context。
  • 销毁录制接口实例
    1. AliyunIMixRecorder#release();

输入输出参数设置

  • 设置合拍视频输入输出出参数(用来参与合拍的样本视频信息,轨道显示信息,输出预览帧率、编码帧率、输出分辨率、编码器类型等)
    1. AliyunIMixRecorder#setMixMediaInfo(AliyunMixMediaInfoParam inputMediaInfo, MediaInfo outputInfo);//相关参数描述请参考AliyunMixMediaInfoParam和MedianInfo的接口文档
  • 设置输出路径
    1. AliyunIMixRecorder#setOutputPath(String path);
  • 设置录制视频质量
    1. AliyunIMixRecorder#setVideoQuality(VideoQuality quality);
  • 设置录制视频的码率
    1. AliyunIMixRecorder#setVideoBitrate(int bitrate);//单位:kbps
  • 设置输出视频的GOP大小
    1. AliyunIMixRecorder#setGop(int gop);//单位:帧数

回调设置

参考视频录制中的接口,接口设计一致。

开启预览

  • 开始预览
    1. AliyunIMixRecorder#startPreview();
  • 结束预览
    1. AliyunIMixRecorder#stopPreview();

录制控制/管理

录制控制

  • 设置录制合拍预览窗口View(SurfaceView)
    1. AliyunIMixRecorder#setDisplayView(SurfaceView previewView, SurfaceView playView);//**注意:这里不能使用GLSurfaceView及其子类。**
    其他摄像头相关控制,参考视频录制中的接口,接口设计一致。

录制管理

  • 获取片段管理器
    1. AliyunIMixRecorder#getClipManager();**注意:如果要删除上一段不能使用AliyunIClipManager里面的接口,而要使用AliyunIMixRecorder#deleteLastPart()。**
  • 设置最大录制时长(总录制时长,不是单个片段的最大时长)
    1. AliyunIClipManager#setMaxDuration(int maxDurationMs);
  • 设置最小录制时长(总录制时长,不是单个片段的时长)
    1. AliyunIClipManager#setMinDuration(int minDurationMs);
  • 获取片段总时长
    1. AliyunIClipManager#getDuration();
  • 获取总的片段数量
    1. AliyunIClipManager#getPartCount();
  • 删除最后一段片段 (非常重要,建议仔细阅读文档)
    1. AliyunIMixRecorder#deleteLastPart();//这里不能使用AliyunIClipManager#deletePart();否则内部的样本视频将不会seek到上一个片段位置。
  • 删除指定的片段
    1. AliyunIClipManager#deletePart(int index);//仅仅用来删除文件,样本视频不会跟随seek。
  • 删除所有片段
    1. AliyunIClipManager#deleteAllPart();//仅仅用来删除片段文件,样本视频不会跟随seek。
  • 获取片段路径列表
    1. AliyunIClipManager#getVideoPathList();

设置特效

参考视频录制中的设置特效,接口设计一致。

开始合拍录制

  • 开始录制一个片段视频

    1. AliyunIMixRecorder#startRecording();
  • 停止录制一个片段视频

    1. AliyunIMixRecorder#stopRecording();

完成合拍录制

  • 结束录制,并且将录制片段视频拼接成一个视频
    1. AliyunIMixRecorder#finishRecording();

示例代码

  1. //合拍一段视频,前置条件:手机SD卡的根目录存在一个视频叫sample.mp4,即/sdcard/sample.mp4,以下代码只描述核心流程,不能将以下代码直接复制粘贴到工程中,详细流程请参考配套的Demo工程。
  2. AliyunIMixRecorder mMixRecorder = AliyunIMixRecorder createAlivcMixRecorderInstance(getApplicationContext());
  3. if(需要所见即所得){
  4. mRecorder.setRecordRotation(0);//这样设置可以保证所见即所得,即横向录制的视频,合拍中依然是横向,不会自动旋转角度。
  5. mRecorder.setFaceDetectRotation(getPictureRotation());//getPictureRotation的代码请参考Demo工程代码。
  6. }else {
  7. mRecorder.setRotation(getPictureRotation());
  8. }
  9. mMixRecorder.setDisplayView(mCameraPrvSurfaceView, mPlayPrvSurfaceView);
  10. AliunIClipManager mClipManager = mRecorder.getClipManager();
  11. mClipManager.setMinDuration(mMinDuration);
  12. mClipManager.setMaxDuration(mMaxDuration);
  13. mRecordTimelineView.setMaxDuration(mClipManager.getMaxDuration());
  14. mRecordTimelineView.setMinDuration(mClipManager.getMinDuration());
  15. MediaInfo mOutputInfo = new MediaInfo();
  16. mOutputInfo.setVideoWidth(outputWidth);
  17. mOutputInfo.setVideoHeight(outputHeight);
  18. AliyunMixRecorderDisplayParam recorderDisplayParam = new AliyunMixRecorderDisplayParam.Builder()
  19. .displayMode(VideoDisplayMode.FILL)
  20. .layoutParam(
  21. new AliyunMixTrackLayoutParam.Builder()
  22. .centerX(0.25f)
  23. .centerY(0.5f)
  24. .widthRatio(0.5f)
  25. .heightRatio(1.0f)
  26. .build()
  27. )
  28. .build();
  29. AliyunMixRecorderDisplayParam sampleDisplayParam = new AliyunMixRecorderDisplayParam
  30. .Builder()
  31. .displayMode(VideoDisplayMode.FILL)
  32. .layoutParam(new AliyunMixTrackLayoutParam.Builder()
  33. .centerX(0.75f)
  34. .centerY(0.5f)
  35. .widthRatio(0.5f)
  36. .heightRatio(1.0f)
  37. .build())
  38. .build();
  39. AliyunMixMediaInfoParam mInputInfo = new AliyunMixMediaInfoParam
  40. .Builder()
  41. .streamStartTimeMills(0L)
  42. .streamEndTimeMills(0L)//设置为0L就会自动使用视频的时长
  43. .mixVideoFilePath("/sdcard/sample.mp4")
  44. .mixDisplayParam(sampleDisplayParam)
  45. .recordDisplayParam(recorderDisplayParam)
  46. .build();
  47. mRecorder.setMixMediaInfo(mInputInfo, mOutputInfo);
  48. mRecorder.setRecordCallback(new RecordCallback() {
  49. @Override
  50. public void onComplete(boolean validClip, long clipDuration) {}
  51. @Override
  52. public void onFinish(String outputPath) {
  53. //合成完成
  54. }
  55. @Override
  56. public void onProgress(final long duration) {}
  57. @Override
  58. public void onMaxDuration() {}
  59. @Override
  60. public void onError(int errorCode) {}
  61. @Override
  62. public void onInitReady() { }
  63. @Override
  64. public void onDrawReady() {}
  65. @Override
  66. public void onPictureBack(Bitmap bitmap) { }
  67. @Override
  68. public void onPictureDataBack(byte[] data) {}
  69. });
  70. mRecorder.startPreview();
  71. mRecorder.startRecording();
  72. mRecorder.stopRecording();
  73. mRecorder.finishRecording();

视频拼接

功能介绍

该接口是一个用于实现离线多画面拼接视频功能的接口,比如画中画,九宫格,左右分屏,上下分屏等效果,支持添加多轨道视频,而不仅仅是合拍录制场景下的双轨道,其功能接口为AliyunIMixComposer

拼接流程

1. 初始化
2. 创建轨道
3. 给轨道添加视频流
4. 配置输出参数
5. 开始混合
6. 销毁

初始化

  1. AliyunMixComposerCreator.createMixComposerInstance();

创建轨道

  1. AliyunIMixComposer#createTrack(AliyunMixTrackLayoutParam layoutParam);

给轨道添加视频流

  • 创建视频流
    1. new AliyunMixStream.Builder().build();
  • 给轨道添加视频流
    1. AliyunMixTrack#addStream(AliyunMixStream stream);

配置输出参数

  1. AliyunIMixComposer#setOutputParam(AliyunMixOutputParam param);

开始混合

  1. AliyunIMixComposer#start(AliyunMixCallback callback);

暂停混合

  1. AliyunIMixComposer#pause();

继续混合

  1. AliyunIMixComposer#resume();

取消混合

  1. AliyunIMixComposer#cancel();

销毁

  1. AliyunIMixComposer#release();

示例代码

  1. //创建实例
  2. AliyunIMixComposer mixComposer = AliyunMixComposerCreator.createMixComposerInstance();
  3. //创建轨道1
  4. AliyunMixTrackLayoutParam track1Layout = new AliyunMixTrackLayoutParam.Builder()
  5. .centerX(0.25f)
  6. .centerY(0.5f)
  7. .widthRatio(0.5f)
  8. .heightRatio(1.f)
  9. .build();
  10. // 创建轨道1的第一个视频流1
  11. AliyunMixStream stream11 = new AliyunMixStream
  12. .Builder()
  13. .displayMode(VideoDisplayMode.FILL)
  14. .filePath("/storage/emulated/0/11.mp4")
  15. .streamEndTimeMills(20000)
  16. .build();
  17. //添加该视频流到轨道1
  18. track1.addStream(stream11);
  19. //创建轨道1的第二个视频流
  20. AliyunMixStream stream12 = new AliyunMixStream
  21. .Builder()
  22. .displayMode(VideoDisplayMode.FILL)
  23. .filePath("/storage/emulated/0/12.mp4")
  24. .streamEndTimeMills(20000)
  25. .build();
  26. //添加该视频流到轨道1
  27. track1.addStream(stream12);
  28. //创建轨道2
  29. AliyunMixTrackLayoutParam track2Layout = new AliyunMixTrackLayoutParam.Builder()
  30. .centerX(0.75f)
  31. .centerY(0.5f)
  32. .widthRatio(0.5f)
  33. .heightRatio(1.f)
  34. .build();
  35. //创建轨道2的第一个视频
  36. AliyunMixStream stream21 = new AliyunMixStream
  37. .Builder()
  38. .displayMode(VideoDisplayMode.FILL)
  39. .filePath("/storage/emulated/0/21.mp4")
  40. .streamEndTimeMills(20000)
  41. .build();
  42. //添加该视频流到轨道2
  43. track2.addStream(stream21);
  44. //创建轨道2的第二个视频
  45. AliyunMixStream stream22 = new AliyunMixStream
  46. .Builder()
  47. .displayMode(VideoDisplayMode.FILL)
  48. .filePath("/storage/emulated/0/22.mp4")
  49. .streamEndTimeMills(20000)
  50. .build();
  51. //添加该视频流到轨道2
  52. track2.addStream(stream22);
  53. //配置输出参数
  54. AliyunMixOutputParam outputParam = new AliyunMixOutputParam.Builder()
  55. .outputPath("/sdcard/output.mp4")
  56. .outputAudioReferenceTrack(track2)//表示使用轨道2的音频作为最后的音频,目前音频轨道只支持一个音频流,因此轨道2第二个视频的音频流不会加进去。
  57. .outputDurationReferenceTrack(track2)//表示使用轨道2的时长作为最后输出视频的时长,如果轨道1的时长不够,则会停在最后一帧
  58. .crf(6)
  59. .videoQuality(VideoQuality.HD)
  60. .outputWidth(720)
  61. .outputHeight(1280)
  62. .fps(30)
  63. .gopSize(30)
  64. .build();
  65. mixComposer.setOutputParam(outputParam);
  66. //开始合成
  67. AliyunMixCallback callback = new AliyunMixCallback() {
  68. @Override
  69. public void onProgress(long progress) {//合成进度
  70. Log.e("MixRecord", "onProgress " + progress);
  71. }
  72. @Override
  73. public void onComplete() {
  74. Log.e("MixRecord", "onComplete");
  75. runOnUiThread(new Runnable() {
  76. @Override
  77. public void run() {
  78. //该接口一定不能在回调的线程中直接调用!!!
  79. mixComposer.release();
  80. }
  81. });
  82. }
  83. @Override
  84. public void onError(int errorCode) {
  85. Log.e("MixRecord", "onError " + errorCode);
  86. }
  87. };
  88. mixComposer.start(callback);