CRI Sofdec  Last Updated: 2024-09-25 20:36 p
サンプル:ループ再生(AV同期調整)

ループ再生(AV同期調整)は サンプル:単純再生 で行っている簡易再生に 追加でオーディオ基準の連結調整の設定を有効化とフレームの間引きを行うようにし、 オーディオとビデオの尺が一致していないムービーをループ再生し続けるサンプルです。

ここではサンプルコードから主要な呼び出し部分を抜粋して、説明します。

1.プレーヤーの設定
・・・
/* ループ再生や連結再生時にAV同期調整を行うように設定 */
criManaPlayer_SetAudioBaseConcatenation(app_obj->player, CRI_TRUE);
/* フレームプール数の設定 */
/* Set the number of frame pools */
criManaPlayer_SetNumberOfFramePools(app_obj->player, NUM_FRAME_POOLS);
/* 入力データのバッファリング時間の指定 */
criManaPlayer_SetBufferingTime(app_obj->player, 2.0f);
・・・
/* 再生の開始 */
/* Start playback */
criManaPlayer_Start(app_obj->player);
void criManaPlayer_SetAudioBaseConcatenation(CriManaPlayerHn player, CriBool flag)
ループ再生や連結再生時にオーディオ基準の連結調整フラグを設定します
void criManaPlayer_SetBufferingTime(CriManaPlayerHn player, CriFloat32 sec)
入力データのバッファリング時間の指定
void criManaPlayer_Start(CriManaPlayerHn player)
再生開始
void criManaPlayer_SetNumberOfFramePools(CriManaPlayerHn player, CriUint32 npools)
内部ビデオバッファー(フレームプール)数の指定
criManaPlayer_Start の再生開始(::criManaPlayer_Prep の再生準備)より前に criManaPlayer_SetAudioBaseConcatenation 関数でループ再生で AV同期調整を行うように設定有効化します。

2.ビデオフレームの取得
CriBool ret = CRI_FALSE;
CriManaFrameInfo frame_info;
CriManaShouldDropFrameReason mana_drop_frame_reason;
CriBool refer_frame_result = CRI_FALSE;
CriUint64 tmp_drop_delay_cnt = app_obj->drop_delay_cnt;
CriUint64 dif_drop_delay_cnt;
app_obj->refer_frame_retry = CRI_FALSE;
for (;;) {
dif_drop_delay_cnt = app_obj->drop_delay_cnt - tmp_drop_delay_cnt;
if (dif_drop_delay_cnt >= NUM_DROP_DELAY_LIMIT) {
/*
* フレーム時刻の遅れが極端な場合、
* フレームの間引きがしばらく続いて動画の描画更新が
* 止まって見えてしまう。
* 本サンプルではリトライ回数に制限を設けて、
* フレームの間引き、描画を交互に繰り返して
* 動画の描画はなるべく止まらないように進めながら
* 少しずつ再生時刻に追いつくようにする。
*/
/* criManaPlayer_IsFrameOnTime の判定へ進める */
break;
}
/* フレームプールにデコード済みのフレームがあるか? */
/* Any decoded frames in the frame pool? */
refer_frame_result = criManaPlayer_ReferFrame(app_obj->player, &frame_info);
if (refer_frame_result == CRI_TRUE) {
/* フレームをドロップすべきかどうか判定 */
if (criManaPlayer_ShouldDropFrame(app_obj->player, &frame_info, NUM_DELAY_FRAMES, &mana_drop_frame_reason) == CRI_TRUE) {
/* フレームをドロップすべきと判定した理由に合わせて処理を行う */
switch(mana_drop_frame_reason) {
/* ビデオフレームの更新(CRIMANA_DROP_FRAME_REASON_DROP_DELAY用) */
app_update_frame_drop_delay(app_obj, &frame_info);
break;
/* ビデオフレームの更新(CRIMANA_DROP_FRAME_REASON_DROP_CONCATENATION用) */
app_update_frame_drop_concatenation(app_obj, &frame_info);
break;
}
if (app_obj->refer_frame_retry == CRI_TRUE) {
/* リトライ処理を行う */
continue;
}
}
}
break;
}
CriManaShouldDropFrameReason
criManaPlayer_ShouldDropFrame判定した理由
Definition: cri_mana.h:530
CriBool criManaPlayer_ShouldDropFrame(CriManaPlayerHn player, CriManaFrameInfo *frame_info, CriSint32 threshold, CriManaShouldDropFrameReason *drop_frame_reason)
フレームをドロップすべきかどうか判定
CriBool criManaPlayer_ReferFrame(CriManaPlayerHn player, CriManaFrameInfo *frame_info)
デコード済みのフレーム情報の参照
@ CRIMANA_DROP_FRAME_REASON_DROP_DELAY
Definition: cri_mana.h:537
@ CRIMANA_DROP_FRAME_REASON_DROP_CONCATENATION
Definition: cri_mana.h:541
ビデオフレーム情報
Definition: cri_mana.h:1364


criManaPlayer_ReferFrame 関数で参照したフレームは、フレームを間引くべきかどうかを criManaPlayer_ShouldDropFrame 関数へ取得したフレームを渡して確認します。 返り値がCRI_TRUEの場合、アプリ側で対象のフレームは間引くように制御します。


3.ビデオフレームをドロップすべき理由( ::CRIMANA_DROP_FRAME_REASON_DROP_DELAY)
/*
* 再生時刻よりフレーム時刻がユーザー指定の閾値:遅延フレーム数より遅れている。
* フレーム時刻の遅れを取り戻して再生時刻に追いつくためにフレームの間引きを行う。
*/
/* 再生時刻よりフレーム時刻がユーザー設定値より遅れている。フレームの間引きを行う。 */
if (app_obj->videoinf->codec_type == CRIMANA_VIDEO_CODEC_SOFDEC_PRIME) {
/* フレームの間引き処理はデコードスキップ機能を利用を推奨 */
if (criMana_GetDecodeSkipFlag() == CRI_FALSE) {
/* 自動デコードスキップが無効なので、手動でデコードスキップを行う */
criManaPlayer_ExecuteAutoSkip(app_obj->player, frame_info);
} else {
/*
* 自動デコードスキップが有効なので、そちらに任せる。
* 自動デコードスキップはcriManaPlayer_IsFrameOnTime の成功判定後の
* criManaPlayer_DiscardFrame内で要求が行われる。
*/
}
/* デコードスキップ機能で制御する場合はリトライのフラグを下げる */
app_obj->refer_frame_retry = CRI_FALSE;
} else {
/* ビデオコーデックがH.264(Androidを除く)、VP9、AV1の場合はデコードスキップできない。 */
/* フレームの間引き処理は明示的にフレーム破棄する。 */
criManaPlayer_DiscardFrame(app_obj->player, frame_info);
/* デバッグ表示用にカウントする */
app_obj->drop_delay_cnt++;
/* フレームの更新は失敗とし、再度criManaPlayer_ReferFrameを試みる */
app_obj->refer_frame_retry = CRI_TRUE;
}
return;
CriBool criMana_GetDecodeSkipFlag(void)
デコードスキップフラグの取得(全ハンドル一括)
void criManaPlayer_ExecuteAutoSkip(CriManaPlayerHn player, CriManaFrameInfo *frame_info)
デコードフレームの自動スキップ判定の実行
void criManaPlayer_DiscardFrame(CriManaPlayerHn player, const CriManaFrameInfo *frame_info)
フレームの解放
@ CRIMANA_VIDEO_CODEC_SOFDEC_PRIME
Definition: cri_mana.h:368


本サンプルでは
criManaPlayer_ShouldDropFrame 関数で得られた CriManaShouldDropFrameReason 構造体の値が CRIMANA_DROP_FRAME_REASON_DROP_DELAY
再生時刻に対してフレーム時刻が遅れているためドロップすべきと判定された場合に細かい制御を行います。

CRIMANA_DROP_FRAME_REASON_DROP_DELAY を取得した場合、デコードスキップをサポートしている環境では
デコード処理自体を省略して無駄が少なくなるため、フレームの間引きをデコードスキップで実現することを推奨しています。

デコードスキップをサポートしていない環境では criManaPlayer_DiscardFrame でフレーム破棄を行って下さい。

フレームの間引きを行った上で先のフレームが得られるまで criManaPlayer_ReferFrame を繰り返すように制御します。

また、フレーム時刻の遅れが非常に大きい場合、フレームの間引きがしばらく続いて動画の描画更新が止まって見えてしまう可能性があります。

アプリ側で止まって見える挙動が好ましくないケースでは、本サンプルのようにリトライ回数に制限を設け、
フレームの間引き、描画を交互に繰り返すことを検討してください。
動画の描画はなるべく止まらないように進めながら少しずつ再生時刻に追いつくようにするような挙動となります。


4.ビデオフレームをドロップすべき理由( ::CRIMANA_DROP_FRAME_REASON_DROP_CONCATENATION)
/*
* ループ再生、連結再生時にAV同期調整用にフレームの間引きを行う。
* オーディオ尺が短く、ビデオ尺が長い場合、
* オーディオ尺以下に収まるようにビデオフレームについてフレーム破棄する。
*(本判定は最大で3枚連続で発生する)
*/
criManaPlayer_DiscardFrame(app_obj->player, frame_info);
/* デバッグ表示用にカウントする */
app_obj->drop_concatenation_cnt++;
/* 再度、criManaPlayer_ReferFrameを試みる */
app_obj->refer_frame_retry = CRI_TRUE;
return;


本サンプルでは criManaPlayer_ShouldDropFrame 関数で得られた CriManaShouldDropFrameReason 構造体の情報が、
CRIMANA_DROP_FRAME_REASON_DROP_CONCATENATION でループ再生や連結再生時にオーディオ基準の連結調整のためドロップすべきと判定された場合に細かい制御を行います。

CRIMANA_DROP_FRAME_REASON_DROP_CONCATENATION では criManaPlayer_DiscardFrame でフレーム破棄を行って下さい。
これはオーディオよりビデオの尺が長いケースでループ再生(あるいは連結再生)の終端のビデオフレームを間引くための処理となります。
本判定は最大で3枚連続で発生する可能性があります。
連結先の先頭フレームが得られるまで criManaPlayer_ReferFrame を繰り返すように制御します。