ショートビデオ SDK は、デュエット録音機能のために AliyunIMixRecorder クラスを提供します。この機能を使用すると、既存のビデオとカメラからのライブ映像を組み合わせて、横並び、上下配置、ピクチャーインピクチャーなどの特定のトラックレイアウトで一緒に録画できます。デュエット録音は基本録画機能の拡張であり、合成に新しいローカルビデオトラックを追加します。
サポートされるエディション
エディション | サポート状況 |
プロフェッショナル | はい |
スタンダード | はい |
ベーシック | いいえ |
基本概念
関連クラス
クラス | 説明 |
デュエット録音のコアクラスです。録画、プレビュー、エフェクト、コールバックのメソッドを提供します。 | |
デュエット録音インスタンスを作成するファクトリークラスです。 | |
トラックレイアウトや出力パスなど、デュエット録音のパラメーターを設定するためのクラスです。 | |
レイアウト情報やレイヤー化など、デュエット録音のトラックを設定するためのクラスです。 | |
動画のディメンション、エンコーダータイプ、キャプチャフレームレートなど、録画パラメーターを設定するためのクラスです。 | |
完了、進捗、エラーコールバックなど、録画コールバックのためのインターフェイスです。 | |
プレビュー解像度の選択、キャプチャされたフレーム、カメラ起動の失敗など、カメラデータキャプチャのコールバックのためのインターフェイスです。 | |
PCM フォーマットで音声データのコールバックを受信するためのインターフェイスです。 | |
クリップの削除や録画時間の設定など、録画されたビデオクリップを管理するためのインターフェイスです。 |
操作手順
デュエット録音を使用するには、ご利用のアプリケーションにカメラとマイクの権限を付与する必要があります。権限がない場合、録画は失敗します。
デュエット録音の操作手順は、基本録画と似ています。主な違いは、入出力パラメーターとプレビュービューの設定にあります。
設定 | ステップ | 説明 | サンプルコード |
基本構成 | 1 | 録画インスタンスの作成、設定、解放を行います。 | |
2 | コールバックを設定します。 | ||
3 | プレビュービューを設定し、プレビューを開始します。 | ||
4 | ビデオクリップの録画を開始、停止、キャンセルします。 | ||
5 | 録画を完了します。 | ||
高度な構成 | 6 | カメラ制御 (カメラタイプやフラッシュモードなど) とビデオクリップ管理 (最大/最小時間やクリップの削除など) を設定します。これらはオプションです。 | |
7 | 美顔やフィルターなどの録画エフェクトを設定します。これらはオプションです。 | ||
8 | 写真撮影や顔認識を設定します。 |
パラメーターの初期化と設定
AliyunIMixRecorder クラスを初期化し、録画インスタンスを作成して、録画パラメーターを設定します。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
初期化
// 録画インスタンスを作成します。
AliyunIMixRecorder recorder = AliyunMixRecorderCreator.createAlivcMixRecorderInstance(context);
// 録画インスタンスを解放します。
// インスタンスが不要になったとき、またはアプリケーションを終了する前に、必ずインスタンスを解放してください。
// 使用中に解放しないでください。
AliyunIMixRecorder.release();録画パラメーターの設定
// 録画ビデオの品質を設定します。
AliyunIMixRecorder.setVideoQuality(quality);
// 録画ビデオのビットレートを設定します。
AliyunIMixRecorder.setVideoBitrate(int bitrate); // 単位:kbps
// 録画ビデオの出力パラメーターを設定します。
// `inputMediaInfo` は、トラックレイアウトや出力ビデオなど、合成ビデオのパラメーターを指定します。
// `outputInfo` は、出力ビデオに関する情報を指定します。
AliyunIMixRecorder.setMixMediaInfo(AliyunMixMediaInfoParam inputMediaInfo, MediaInfo outputInfo); // パラメーターの詳細については、AliyunMixMediaInfoParam および MediaInfo の API リファレンスをご参照ください。
// 録画ビデオの出力パスを設定します。
AliyunIMixRecorder.setOutputPath(String path);
// 出力ビデオの Group of Pictures (GOP) サイズを設定します。
AliyunIMixRecorder.setGop(int gop); // 単位:フレーム数コールバックの設定
音声およびビデオ処理の進捗とステータスに関するリアルタイムの更新を受け取るために、コールバックを設定します。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
// 録画コールバックを設定します。
AliyunIMixRecorder.setRecordCallBack(RecordCallback callBack);
// キャプチャされたビデオフレームのコールバックを設定します。
AliyunIMixRecorder.setOnFrameCallback(OnFrameCallBack callback);
// キャプチャされた音声データのコールバックを設定します。
AliyunIMixRecorder.setOnAudioCallback(OnAudioCallBack callback);プレビューの有効化
プレビューを有効にするには、SurfaceView コンテナを提供する必要があります。ご利用の Activity/Fragment の onResume() メソッドで startPreview() を呼び出し、onPause() メソッドで stopPreview() を呼び出します。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
// プレビュービューを設定します。
// `cameraView` はカメラフィード用のビュー、`videoView` はローカルビデオ用のビューです。
AliyunIMixRecorder.setDisplayView(SurfaceView cameraView, Surface videoView);
// プレビューを開始します。
AliyunIMixRecorder.startPreview();
// プレビューを停止します。
// ご利用の Activity/Fragment の onPause() メソッドで stopPreview() を呼び出します。
AliyunIMixRecorder.stopPreview();録画の開始
最終的なビデオを作成するには、多くの場合、録画の開始、停止、再開を複数回行う必要があります。録画を停止するとビデオクリップが生成され、録画をキャンセルすると現在のクリップが破棄されます。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
録画の開始
// 録画を開始します。
AliyunIMixRecorder.startRecording();クリップの録画
// 最初のクリップの録画を開始します。
AliyunIMixRecorder.startRecording();
// 録画を停止してビデオクリップを生成します。
AliyunIMixRecorder.stopRecording();
// 別のクリップの録画を開始します。
AliyunIMixRecorder.startRecording();
// 録画をキャンセルし、現在のクリップを破棄します。
AliyunIMixRecorder.cancelRecording();
// 次のビデオクリップの録画を続行します。
AliyunIMixRecorder.startRecording();
AliyunIMixRecorder.stopRecording();録画の完了
録画が完了したら、すべてのクリップを 1 つのビデオファイルにマージするか、クリップに関する情報を含む設定ファイルを生成することができます。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
// 録画を完了し、録画されたすべてのクリップを 1 つのビデオファイルにマージします。
AliyunIMixRecorder.finishRecording();
// 録画を完了し、クリップをマージせずにクリップの設定ファイルを生成します。
AliyunIMixRecorder.finishRecordingForEdit();録画の制御とクリップの管理
このセクションでは、カメラ制御 (カメラタイプやフラッシュモードなど) とビデオクリップ管理 (録画時間の設定、クリップの削除、クリップ数の取得など) のオプションパラメーターを設定する方法について説明します。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
カメラ制御
// 利用可能なカメラの数を取得します。
AliyunIMixRecorder.getCameraCount();
// カメラタイプを設定します。
AliyunIMixRecorder.setCamera(cameraType);
// 録画をミュートします。
AliyunIMixRecorder.setMute(boolean isMute);
// センサーの回転角度を設定します。
// 重要:このパラメーターについては、API ドキュメントをよくお読みください。
AliyunIMixRecorder.setRotation(int rotation);
// 録画の回転角度を設定します。
// 重要:このパラメーターについては、API ドキュメントをよくお読みください。
AliyunIMixRecorder.setRecordRotation(int rotation);
// フラッシュ、フォーカスモード、ズーム、露出などのカメラプレビューパラメーターを設定します。
// これらのパラメーターは、以下のメソッドを使用して個別に設定することもできます。
AliyunIMixRecorder.setCameraParam(CameraParam cameraParam);
// フロントカメラとリアカメラを切り替えます。
AliyunIMixRecorder.switchCamera();
// フラッシュモードを設定します。
AliyunIMixRecorder.setLight(FlashType flashType);
// ズームレベルを設定します。
AliyunIMixRecorder.setZoom(float rate);
// 露出補正率を設定します。
AliyunIMixRecorder.setExposureCompensationRatio(float value);
// フォーカスモードを設定します。
AliyunIMixRecorder.setFocusMode(int mode);
// 特定のポイントで手動フォーカスをトリガーします。
AliyunIMixRecorder.setFocus(float xRatio, float yRatio);クリップ管理
// クリップマネージャーインスタンスを取得します。
AliyunIClipManager manager = AliyunIRecorder.getClipManager();
// 録画の最大合計時間をミリ秒単位で設定します (単一クリップ用ではありません)。
manager.setMaxDuration(int maxDurationMs);
// 録画の最小合計時間をミリ秒単位で設定します (単一クリップ用ではありません)。
manager.setMinDuration(int minDurationMs);
// 最後に録画したクリップを削除します。
manager.deletePart();
// 特定のインデックスのクリップを削除します。
manager.deletePart(int index);
// 録画されたすべてのクリップを削除します。
manager.deleteAllPart();
// すべてのクリップの合計時間を取得します。
manager.getDuration();
// クリップの総数を取得します。
manager.getPartCount();
// すべてのクリップのファイルパスのリストを取得します。
manager.getVideoPathList();エフェクトの設定
このセクションでは、美顔やフィルターなどのオプションの録画エフェクトを設定する方法について説明します。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
フィルター
カスタムフィルターを作成できます。詳細については、「フィルターとトランジション」をご参照ください。
// フィルターを適用します。
AliyunIMixRecorder.applyFilter(effectFilter);
// フィルターを削除します。
// フィルターを削除するには、パスが null の新しい EffectFilter を渡します。
AliyunIMixRecorder.applyFilter(new EffectFilter(null));アニメーションフィルター
// アニメーションフィルターを適用します。
AliyunIMixRecorder.applyAnimationFilter(effectFilter);
// アニメーションフィルターを削除します。
AliyunIMixRecorder.removeAnimationFilter(effctFilter);録画速度
// 録画速度を設定します。
AliyunIMixRecorder.setRate(float rate);静的ステッカーとウォーターマーク
// 静的ステッカーまたはウォーターマークを追加します。
AliyunIMixRecorder.addImage(effctImage);
// 静的ステッカーまたはウォーターマークを削除します。
AliyunIMixRecorder.removeImage(effctImage);
// 静的ステッカーまたはウォーターマークの位置を更新します。
AliyunIMixRecorder.setEffectView(float xRatio,float yRatio,float widthRatio,float heightRatio,EffectBase effectBase);動的ステッカー
カスタムの動的ステッカーを作成できます。詳細については、「アニメーション画像」をご参照ください。
// 動的ステッカーを追加します。
AliyunIMixRecorder.addPaster(effectPaster,float sx,float sy,float sw,float sh,float rotation,boolean flip);
// 動的ステッカーを削除します。
AliyunIMixRecorder.removePaster(effectPaster);
// 動的ステッカーの位置を更新します。
AliyunIMixRecorder.setEffectView(float xRatio,float yRatio,float widthRatio,float heightRatio,effectBase);高度なレタッチ
ビデオ録画モジュールは、基本的な内蔵美顔機能を提供し、Alibaba Cloud 美顔エフェクト SDK や FaceUnity などのサードパーティの美顔 SDK もサポートしています。内蔵の美顔機能はシンプルで、美顔レベルの設定のみが可能です。通常、サードパーティの美顔 SDK は、高度な美顔、顔の補正、メイクアップエフェクト、フィルター、ステッカーなど、より豊富な機能セットを提供します。
内蔵レタッチ
// 美顔を有効または無効にします。 AliyunIMixRecorder.setBeautyStatus(boolean on); // 美顔レベルを設定します。 AliyunIMixRecorder.setBeautyLevel(int level);サードパーティの美顔 SDK
サードパーティの美顔 SDK のエフェクトを使用するには、まず必要な権限を取得し、SDK をショートビデオ SDK と統合する必要があります。
Alibaba Cloud 美顔エフェクト SDK の統合については、「美顔エフェクト SDK」をご参照ください。エフェクトの設定方法に関するコード例については、「使用例」をご参照ください。
FaceUnity SDK の購入、統合、使用については、「FaceUnity」をご参照ください。
サードパーティの美顔 SDK が機能するには、カメラのテクスチャ ID とカメラの生フレームデータという 2 種類のデータが必要です。以下のコードは、このデータを取得する方法を示しています。
カメラのテクスチャ ID データを取得
AliyunIMixRecorder.setOnTextureIdCallback(new OnTextureIdCallBack() { @Override public int onTextureIdBack(int textureId, int textureWidth, int textureHeight, float[] matrix) { if (mBeautyInterface != null) { return mBeautyInterface.onTextureIdBack(textureId, textureWidth, textureHeight, matrix, mControlView.getCameraType().getType()); } return textureId; } @Override public int onScaledIdBack(int scaledId, int textureWidth, int textureHeight, float[] matrix) { return scaledId; } @Override public void onTextureDestroyed() { // カスタム (サードパーティ) レンダリングの GL リソースを破棄するには: // 3.7.8 より前の SDK バージョンでは、GLSurfaceView.queueEvent を使用できます。 // SDK バージョン 3.7.8 以降では、このコールバック内で GL リソースを破棄することを推奨します。 if (mBeautyInterface != null) { mBeautyInterface.release(); mBeautyInterface = null; } } });カメラの生フレームデータを取得
AliyunIMixRecorder.setOnFrameCallback(new OnFrameCallBack() { @Override public void onFrameBack(byte[] bytes, int width, int height, Camera.CameraInfo info) { // 生データのコールバックは NV21 フォーマットです。この生データは主に、 // FaceUnity などのサードパーティ SDK が高度な美顔処理に使用します。 if (mBeautyInterface != null) { mBeautyInterface.onFrameBack(bytes, width, height, info); } } @Override public Camera.Size onChoosePreviewSize(List<Camera.Size> supportedPreviewSizes, Camera.Size preferredPreviewSizeForVideo) { return null; } @Override public void openFailed() { } });
その他の機能
SDK は、ビデオ録画中の写真撮影と顔認識をサポートしています。コードで使用されるパラメーターの詳細については、「関連クラス」をご参照ください。
写真撮影
エフェクトあり、またはエフェクトなしで写真を撮ることができます。エフェクトなしで撮影された写真は、システムのカメラ機能を使用します。写真データは RecordCallback.onPictureBack(Bitmap) または RecordCallback.onPictureDataBack(byte[]) コールバックを介して返されます。
// エフェクトを適用して写真を撮ります。
AliyunIMixRecorder.takePhoto(boolean needBitmap);
// システムカメラを使用して写真を撮ります (エフェクトなし)。
AliyunIMixRecorder.takePicture(boolean needBitmap);
// システム写真の写真サイズを設定します。これはエフェクト付きの写真ではサポートされていません。
AliyunIMixRecorder.setPictureSize(Camera.Size size);顔認識
顔認識を使用するには、アプリ独自の顔認識モデルファイルを統合する必要があります。例については、顔認識モデルファイルのデモをご参照ください。
// 顔認識を有効または無効にします。
AliyunIMixRecorder.needFaceTrackInternal(boolean need);
// 顔認識モデルファイルへのパスを設定します。
AliyunIMixRecorder.setFaceTrackInternalModelPath(String path);
// 顔検出の回転角度を設定します。
// 重要:このパラメーターについては、API ドキュメントをよくお読みください。
AliyunIMixRecorder.setFaceDetectRotation(int rotation);
// 検出する顔の最大数を設定します。
// 内蔵の顔追跡の最大値は 3 です。
AliyunIMixRecorder.setFaceTrackInternalMaxFaceCount(int maxFaceCount);
// 顔を追跡する動的ステッカーを追加します。
AliyunIMixRecorder.addPaster(EffectPaster effectPaster);デュエット録音のサンプルコード
import android.graphics.Bitmap
import android.os.Bundle
import android.util.Log
import android.view.SurfaceView
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.aliyun.svideosdk.common.struct.common.VideoDisplayMode
import com.aliyun.svideosdk.common.struct.encoder.VideoCodecs
import com.aliyun.svideosdk.common.struct.recorder.MediaInfo
import com.aliyun.svideosdk.mixrecorder.AliyunIMixRecorder
import com.aliyun.svideosdk.mixrecorder.AliyunMixMediaInfoParam
import com.aliyun.svideosdk.mixrecorder.AliyunMixRecorderDisplayParam
import com.aliyun.svideosdk.mixrecorder.AliyunMixTrackLayoutParam
import com.aliyun.svideosdk.mixrecorder.impl.AliyunMixRecorderCreator
import com.aliyun.svideosdk.recorder.RecordCallback
/**
* ビデオ録画の例
*/
class MixRecordActivity : AppCompatActivity() {
enum class RecordStatus {
Idle,
Recording
}
private lateinit var mAliyunRecord : AliyunIMixRecorder
private lateinit var mVideoPreviewView : SurfaceView
private lateinit var mCameraPreviewView : SurfaceView
private lateinit var mRecordBtn : ImageView
private var mRecordStatus = RecordStatus.Idle
companion object {
const val TAG = "MixRecordActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_mixrecord)
mVideoPreviewView = findViewById(R.id.videoPreviewView)
mCameraPreviewView = findViewById(R.id.cameraPreviewView)
mRecordBtn = findViewById(R.id.btnRecordControl)
// 録画ボタン
mRecordBtn.setOnClickListener {
if(mRecordStatus == RecordStatus.Recording) {
mAliyunRecord.finishRecording()
Toast.makeText(this@MixRecordActivity, "Stop recording", Toast.LENGTH_SHORT).show()
updateRecordStatus(RecordStatus.Idle)
} else {
val curTime = System.currentTimeMillis()
mAliyunRecord.setOutputPath("/storage/emulated/0/DCIM/Camera/svideo_mixrecord_video_$curTime.mp4")
mAliyunRecord.startRecording()
Toast.makeText(this@MixRecordActivity, "Start recording", Toast.LENGTH_SHORT).show()
updateRecordStatus(RecordStatus.Recording)
}
}
mAliyunRecord = AliyunMixRecorderCreator.createAlivcMixRecorderInstance(this)
val videoDisplayParam = AliyunMixRecorderDisplayParam.Builder()
.displayMode(VideoDisplayMode.FILL)
.layoutParam(
AliyunMixTrackLayoutParam.Builder()
.centerX(0.25f)
.centerY(0.5f)
.widthRatio(0.5f)
.heightRatio(1.0f)
.build()
)
.build()
val cameraDisplayParam = AliyunMixRecorderDisplayParam.Builder()
.displayMode(VideoDisplayMode.FILL)
.layoutParam(AliyunMixTrackLayoutParam.Builder()
.centerX(0.75f)
.centerY(0.5f)
.widthRatio(0.5f)
.heightRatio(1.0f)
.build())
.build()
val mixMediaParam = AliyunMixMediaInfoParam.Builder()
.streamStartTimeMills(0L)
.streamEndTimeMills(0L) // 0L に設定すると、ビデオの全時間が自動的に使用されます。
.mixVideoFilePath("/storage/emulated/0/DCIM/Camera/VID_20210317_174802.mp4")
.mixDisplayParam(videoDisplayParam)
.recordDisplayParam(cameraDisplayParam)
.build()
val mediaInfo = MediaInfo()
mediaInfo.fps = 30
mediaInfo.crf = 6
mediaInfo.videoWidth = 720
mediaInfo.videoHeight = 1080
mediaInfo.videoCodec = VideoCodecs.H264_SOFT_OPENH264
mAliyunRecord.setMixMediaInfo(mixMediaParam, mediaInfo)
mAliyunRecord.setDisplayView(mCameraPreviewView, mVideoPreviewView)
mAliyunRecord.setRecordCallback(object : RecordCallback {
override fun onComplete(validClip: Boolean, clipDuration: Long) {
Log.i(TAG, "onComplete")
}
override fun onFinish(outputPath: String?) {
Log.i(TAG, "onFinish path : $outputPath")
mAliyunRecord.clipManager.deleteAllPart()
}
override fun onProgress(progress: Long) {
Log.i(TAG, "onProgress : $progress")
}
override fun onMaxDuration() {
Log.i(TAG, "onMaxDuration")
}
override fun onError(errorCode: Int) {
Log.i(TAG, "onError : $errorCode")
}
override fun onInitReady() {
Log.i(TAG, "onInitReady")
}
override fun onDrawReady() {
Log.i(TAG, "onDrawReady")
}
override fun onPictureBack(bitmap: Bitmap?) {
Log.i(TAG, "onPictureBack")
}
override fun onPictureDataBack(p0: ByteArray?) {
Log.i(TAG, "onPictureDataBack")
}
})
}
private fun updateRecordStatus(recordStatus: RecordStatus)
{
mRecordStatus = recordStatus
when(recordStatus) {
RecordStatus.Idle -> {
mRecordBtn.setImageResource(R.mipmap.alivc_svideo_bg_record_start)
}
RecordStatus.Recording -> {
mRecordBtn.setImageResource(R.mipmap.alivc_svideo_bg_record_storp)
}
}
}
override fun onResume() {
super.onResume()
mAliyunRecord.startPreview()
}
override fun onPause() {
super.onPause()
mAliyunRecord.stopPreview()
}
override fun onDestroy() {
super.onDestroy()
mAliyunRecord.release()
}
}XML 設定例
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<SurfaceView
android:id="@+id/videoPreviewView"
android:layout_width="0dp"
android:layout_height="400dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline3"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="0dp" />
<SurfaceView
android:id="@+id/cameraPreviewView"
android:layout_width="0dp"
android:layout_height="400dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/btnRecordControl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginBottom="50dp"
android:src="@mipmap/alivc_svideo_bg_record_start"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</ImageView>
</androidx.constraintlayout.widget.ConstraintLayout>