このトピックでは、ApsaraVideo Player SDK for Androidに関するよくある質問に対する回答を提供します。
現在の再生位置を取得するにはどうすればよいですか?
デフォルトでは、ApsaraVideo Player SDKは500ミリ秒ごとに現在の再生位置に関するコールバックを返します。 再生位置をより頻繁に取得するために、コールバックが返される間隔を短くすることができます。 サンプルコード:
// Change the callback interval.
PlayerConfig config = mAliyunLivePlayer.getConfig();
config.mPositionTimerIntervalMs = 100; // The callback interval. Unit: milliseconds.
mAliyunLivePlayer.setConfig(config);
mAliPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
@Override
public void onInfo(InfoBean infoBean) {
if(infoBean.getCode() == InfoCode.CurrentPosition){
// The current playback position.
long currentPosition = infoBean.getExtraValue();
}
}
});
ApsaraVideoプレーヤーを作成するときにクラッシュが発生した場合はどうすればよいですか?
問題をトラブルシューティングするには、次の手順を実行します。
CPUがx86アーキテクチャを使用しているかどうかを確認します。
ApsaraVideo Player SDKは、arm64-v8aおよびarmeabi-v7aアーキテクチャのみをサポートします。 x86アーキテクチャはサポートされていません。
両方が ファイルとMavenの依存関係がプロジェクトに追加されます。
たとえば、Mavenの依存関係をbuild.gradleファイルに追加してApsaraVideo Player SDKを統合し、SDKの動的ライブラリをlibsディレクトリに同時に追加すると、クラッシュが発生します。
解決策: Maven依存関係と動的ライブラリの両方がプロジェクトに追加されている場合は、動的ライブラリを削除することをお勧めします。 動的ライブラリをプロジェクトに追加する必要がある場合は、そのため、ファイルは同じSDKバージョンです。 ApsaraVideo Player SDKを統合して動的ライブラリを取得する方法の詳細については、「クイック統合」をご参照ください。 次の図は、ApsaraVideo Player SDKの動的ライブラリを示しています。
部品パッケージを使用してSDKを統合する場合は、AlivcFFmpegの正しいバージョンがプロジェクトに追加されていることを確認してください。
詳細については、「AlivcFFmpeg依存関係」をご参照ください。
ApsaraVideo Playerの実行中にクラッシュが発生した場合はどうすればよいですか?
問題をトラブルシューティングするには、次の手順を実行します。
ApsaraVideo Player SDKでクラッシュが発生していないか確認します。
AliyunPlayer
というプレフィックスが付いたクラッシュスタック名が存在する場合、ApsaraVideo Player SDKでクラッシュが発生します。ApsaraVideo Player SDKを最新バージョンに更新し、問題が修正されたかどうかを確認します。
問題が解決しない場合は、Alibaba Cloudテクニカルサポートにお問い合わせください。 テクニカルサポートに連絡するときに、すべてのスレッドとクラッシュログに関するファイルを準備し、クラッシュシナリオを記述します。 詳細については、「エラーログを取得するにはどうすればよいですか?」をご参照ください。
ビデオの再生中に黒いバーが表示された場合はどうすればよいですか?
問題をトラブルシューティングするには、次の手順を実行します。
ビデオソースに黒いバーが表示されていないか確認します。
次の操作を呼び出して、プレーヤーのスケーリングモードを変更します。
/* SCALE_ASPECT_FILL: crops the video to fit the view based on the aspect ratio of the video. SCALE_ASPECT_FIT: scales in the video based on the aspect ratio of the video. Black bars are displayed in the view. SCALE_TO_FILL: stretches the video to fill in the view. Image distortion may occur. */ mAliPlayer.setScaleMode();
スケーリングモードを変更しても問題が解決しない場合は、アプリケーションレイヤーでSurfaceViewまたはTextureViewを呼び出して、ビューのサイズを変更します。
オーディオが再生されているのにビデオ画像が表示されない場合はどうすればよいですか?
問題をトラブルシューティングするには、次の手順を実行します。
別のプレーヤーでビデオを再生し、ビデオにオーディオのみが含まれているかどうかを確認します。
ビューが正しく設定されているかどうかを確認します。 たとえば、ビューが設定されていないか、再生ウィンドウの外に配置されていない場合、この問題が発生します。 ビューの設定方法の詳細については、「プレーヤーの作成」の手順4をご参照ください。
読み取り権限があるローカルビデオを再生したときにエラーメッセージ「Invalid argument」が返された場合はどうすればよいですか?
ファイルの名前と絶対パスを確認して、パスに漢字とスペースが含まれていないことを確認してください。
読み取り権限があるローカルビデオを再生したときにエラーメッセージ「Permission denied」が返された場合はどうすればよいですか?
Androidは、Android 10 (Android Q) 以降、スコープ付きストレージ機能を導入しています。 したがって、androidデバイスのストレージ権限を使用する前に、AndroidManifest.xml
ファイルのapplication要素にAndroid: requestLegacyExternalStorage="true"
属性を追加する必要があります。
ビデオがフルスクリーンモードで再生されているときに、ノッチ付きディスプレイの上部に黒い通知バーが表示された場合はどうすればよいですか?
イマーシブステータスバーを有効にして、問題を解決できます。
MOVビデオの再生に失敗した場合はどうすればよいですか?
ApsaraVideo Player SDK for Androidは、MOVビデオの再生をサポートしています。 MOVビデオの再生に失敗した場合、ソースビデオのmdatアトムがmoovアトムの前に配置されているかどうかを確認します。 mdatアトムはメディアデータを含み、moovアトムはメディアデータのインデックスとして機能する。 ソースビデオのmdatアトムがmoovアトムの前に配置されている場合、ソースビデオをトランスコードして、moovアトムをmoovアトムの前に配置します。 詳細については、「手順2: メディアストリームの確認」をご参照ください。
それを示すエラーがある場合はどうしますか。プレーヤーの初期化または再生中にApsaraVideo Player SDKのファイルが見つかりませんか?
問題をトラブルシューティングするには、次の手順を実行します。
CPUアーキテクチャが要件を満たしているかどうかを確認します。
ApsaraVideo Player SDKは、arm64-v8aおよびarmeabi-v7aアーキテクチャのみをサポートします。
Apsaravideo Player SDKのバージョンが古いかどうかを確認します。
使用するApsaraVideo Player SDKのバージョンが5.4.6.0-full以前の場合、SDKを5.4.6.0-full-15467853以降に更新することを推奨します。 ApsaraVideo Player SDKのバージョンの詳細については、「ApsaraVideo Player SDK For Androidのリリースノート」をご参照ください。
AliListPlayerを使用してHLS (M3U8) ビデオを再生するときにエラーが発生した場合はどうすればよいですか?
ApsaraVideo Player SDK V5.4.5.0以前を使用している場合、AliListPlayer
を使用してHLS (M3U8) ビデオを再生することはできません。 ApsaraVideo Player SDK V5.4.5.0以降を使用する場合、HLS (M3U8) ビデオを再生するにはローカルキャッシュを有効にする必要があります。 詳細については、「ローカルキャッシュの設定」をご参照ください。
ApsaraVideo VOD Player SDK for Androidを使用して、Androidプロジェクトのアセットまたはrawフォルダーでビデオを再生できますか?
いいえ。 ビデオをモバイルデバイスに保存し、絶対パスでビデオを再生する必要があります。
ローカルキャッシュを有効にし、HLSストリームの再生を試みました。 再生が失敗し、HTTPステータスコードの403が返されます。 どうすればこれを修正できますか?
問題の説明: VidAuthメソッドに基づくHTTPライブストリーミング (HLS) ビデオストリームの再生が失敗し、ローカルキャッシュが有効になった後にHTTPステータスコード403が返されます。
原因: ビデオのローカルキャッシュを有効にして、ビデオが完全にキャッシュされる前に再生を停止した場合、再生を再開するときに、以前の再生で使用された認証情報が引き続き使用されます。 この場合、認証は失敗し、HTTPステータスコード403が返されます。
解決策: ApsaraVideo Player SDK V5.4.5.0以降を使用し、HLSビデオの再生URLに認証パラメーターが含まれている場合、PlayerConfig.mEnableStrictAuthMode
を設定して認証モードを指定できます。 デフォルトでは、PlayerConfig.mEnableStrictAuthModeはfalseに設定されています。 有効な値:
false: ビデオコンテンツをキャッシュし、認証を実行します。 ビデオが完全にキャッシュされていない場合、プレーヤーは、キャッシュされていないビデオコンテンツを再生するときに、キャッシュされたコンテンツに関する認証情報を使用してURL署名要求を送信します。 署名付きURLの有効期間が短すぎると、再生エラーが発生します。
true: 認証を実行し、ビデオコンテンツをキャッシュしません。 再生ごとにURL署名が必要です。 ネットワーク接続が再生の失敗につながることはありません。
ApsaraVideo Player SDK for Androidは、プレイアンドキャッシュ機能をサポートしていますか?
いいえ。 ApsaraVideo Player SDK for Androidでローカルキャッシングを有効にして、再生中にビデオファイルをローカルマシンにダウンロードできます。 その後、ApsaraVideo Player SDK for Androidを使用して、ダウンロードしたビデオファイルを再生できます。 ApsaraVideo Player SDKを使用してダウンロード中のファイルを再生することはできません。
ApsaraVideo Player SDK for Androidを使用して、ビデオのバッファリングの進行状況を取得できますか?
はい。 ApsaraVideo Player SDK for Androidでは、バッファリング速度、リアルタイムのレンダリングフレームレート、オーディオとビデオのビットレート、およびダウンリンクビットレートを取得できます。 詳細については、「再生情報の取得」をご参照ください。
HDRビデオを再生するときに例外が発生するのはなぜですか?
ApsaraVideo VOD Player SDK for Androidは、回転パラメータが設定されているハイダイナミックレンジ (HDR) ビデオをサポートしていません。 したがって、回転パラメータが設定されているHDRビデオを再生すると、例外が発生する可能性があります。
シーク機能に関するFAQ
進捗ハンドルがシークポジションからスキップした場合はどうすればよいですか?
原因: 不正確なシークモードがプレーヤーで使用されています。 この場合、ビデオの再生は、目的の位置に最も近いキーフレームから開始されます。
解決策: 正確なシークモードに切り替えます。
正確な探索モードと不正確な探索モードを切り替えるにはどうすればよいですか?
次のサンプルコードは、シークモードを切り替える方法の例を示しています。
// Switch to the inaccurate seeking mode.
mAliPlayer.seekTo(1000);
mAliPlayer.seekTo(1000, IPlayer.SeekMode.Inaccurate);
// Switch to the accurate seeking mode.
mAliPlayer.seekTo(1000,IPlayer.SeekMode.Accurate);
正確な探索モードが使用されているときに、進捗ハンドルが探索位置からスキップする場合はどうすればよいですか?
原因: 正確なシークは、不正確なシークよりも完了に時間がかかります。 指定された位置と最も近いキーフレームとの間隔が正確なシークモードで許容される最大間隔よりも長い再生位置にシークすると、ApsaraVideo Player SDKは自動的に不正確なシークモードを使用してシーク操作を行います。
解決策: setMaxAccurateSeekDelta操作を呼び出して、正確な探索モードで許可されている探索位置と最も近いキーフレームとの間の最大間隔を設定します。 このように、シーク精度は改善されるが、応答時間はより長くなる。 ビジネス要件に基づいて最大間隔を設定することを推奨します。 サンプルコード:
// Unit: milliseconds.
mAliPlayer.setMaxAccurateSeekDelta(1000);
キャッシュに関するFAQ
キャッシュ再生機能を使用すると、キャッシュが失敗した場合はどうすればよいですか?
onInfoコールバックでキャッシュ失敗の原因を取得します。 次のサンプルコードは、onInfoコールバックを設定する方法の例を示しています。
mAliPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
@Override
public void onInfo(InfoBean infoBean) {
if(infoBean.getCode() == InfoCode.CacheSuccess){
// Caching during playback succeeded.
}else if(infoBean.getCode() == InfoCode.CacheError){
// Caching during playback failed.
String error = infoBean.getExtraMsg();
}
}
});
次の項目は、キャッシュエラーの一般的な原因を示しています。
CacheConfigで指定されたサイズとデュレーションは、ビデオのサイズと一致しません。
再生中に停止またはシークする位置は、バッファの外にあります。
ApsaraVideo Player SDK V5.4.4.0にプリロード機能とローカルキャッシュ機能が追加されました。 これらの機能は、再生およびキャッシュ機能と同じ機能を提供します。 プリロード機能とローカルキャッシュ機能の使用を推奨します。 詳細については、「高度な機能」をご参照ください。
ローカルキャッシュ機能を使用するときに、内部キャッシュディレクトリを指定できますか?
はい。 Androidデバイスの外部ストレージディレクトリを内部ストレージディレクトリに置き換えることができます。 内部ストレージディレクトリにアクセスする権限があることを確認してください。
ビデオキャッシング中にエラーメッセージ「encrypt check fail」が返された場合はどうすればよいですか?
セキュアなダウンロードを有効にしている場合は、暗号化検証用のセキュリティファイルがアプリと一致するかどうかを確認します。 セキュリティファイルをダウンロードし、ApsaraVideo SDKが保存されているディレクトリに保存する必要があります。 そうしないと、キャッシュまたはダウンロードが失敗する可能性があります。 詳細については、「ダウンロード設定の設定」および「ビデオダウンロードの設定」をご参照ください。
メディアソースデータを取得するにはどうすればよいですか?
メディアソースデータは、ソフトウェアデコードを使用して暗号化されていないビデオを再生する場合にのみ返されます。 サンプルコード:
// Switch to software decoding.
mAliPlayer.enableHardwareDecoder(false);
IPlayer.RenderFrameCallbackConfig renderFrameCallbackConfig = new IPlayer.RenderFrameCallbackConfig();
// Specifies whether to return only the address of the underlying video data. Default value: true.
renderFrameCallbackConfig.mVideoDataAddr = false;
// Specifies whether to return only the address of the underlying audio data. Default value: false.
renderFrameCallbackConfig.mAudioDataAddr = false;
mAliPlayer.setRenderFrameCallbackConfig(renderFrameCallbackConfig);
mAliPlayer.setOnRenderFrameCallback(new IPlayer.OnRenderFrameCallback() {
@Override
public boolean onRenderFrame(FrameInfo frameInfo) {
return false;
}
});
ビデオの幅と高さはどのように取得できますか?
次のいずれかの方法を使用して、ビデオの高さと幅を取得できます。
AliPlayerインスタンスでprepareメソッドを呼び出します。 AVPEventPrepareDoneイベントが完了したら、AliPlayerの高さと幅を取得します。 サンプルコード:
mAliyunPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() { @Override public void onPrepared() { mAliyunPlayer.getVideoWidth(); mAliyunPlayer.getVideoHeight(); } });
ビデオサイズが変更されたときにトリガーされるコールバックをリッスンします。
mAliyunPlayer.setOnVideoSizeChangedListener(new IPlayer.OnVideoSizeChangedListener() { @Override public void onVideoSizeChanged(int width, int height) { } });
トラック情報を取得するためのコールバックを設定します。
mAliyunPlayer.setOnTrackReadyListener(new IPlayer.OnTrackReadyListener() { @Override public void onTrackReady(MediaInfo mediaInfo) { List<TrackInfo> trackInfos = mediaInfo.getTrackInfos(); for (TrackInfo trackInfo : trackInfos) { if(trackInfo.getType() == TrackInfo.Type.TYPE_VIDEO){ trackInfo.getVideoWidth(); trackInfo.getVideoHeight(); } } } });
自動ビットレート切り替えのルールは何ですか?
mAliPlayer.selectTrack(TrackInfo.AUTO_SELECT_INDEX)
を呼び出して自動ビットレート切り替え機能を有効にすると、ApsaraVideo Player SDKはネットワーク速度を監視します。 ビデオのビットレートは、10秒の平均ネットワーク速度に基づいて、より高いビットレートとより低いビットレートとの間で切り替えられる。 10秒の平均ネットワーク速度が条件を満たさない場合、ビデオビットレートは切り替えられません。
10秒の平均ネットワーク速度がより低いビットレートの条件を満たす場合、キャッシュされたコンテンツの再生が完了した後、ビデオはより低いビットレートに切り替えられます。
10秒の平均ネットワーク速度がより高いビットレートの条件を満たす場合、ビデオはすぐにより高いビットレートに切り替えられます。
自動ビットレート切り替え機能を使用するには、ApsaraVideo VODコンソールを使用してビデオをアダプティブストリームにトランスコードし、アダプティブストリームを再生するプレーヤーを指定する必要があります。 次のサンプルコードは、VidAuthベースの例を示しています。
VidAuth vidAuth = new VidAuth();
List<Definition> list = new ArrayList<>();
list.add(Definition.DEFINITION_AUTO);
vidAuth.setDefinition(list);
再試行ロジックをカスタマイズするにはどうすればよいですか?
15秒後にネットワーク接続がタイムアウトすると、ApsaraVideo Player SDKはネットワークの再接続を試みます。 デフォルトでは、エラーコールバックが発生する前に2回の再試行が実行されます。
再試行ロジックをカスタマイズし、再試行イベントが発生したときに通知を受け取ることができます。 サンプルコード:
PlayerConfig config = mAliPlayer.getConfig();
// 1. Specify the number of retries. Set the value to 0 in this example.
config.mNetworkRetryCount = 0;
mAliPlayer.setConfig(config);
mAliPlayer.setOnInfoListener(new IPlayer.OnInfoListener() {
@Override
public void onInfo(InfoBean infoBean) {
// 2. Listen to the retry event.
if(infoBean.getCode() == InfoCode.NetworkRetry){
// Configure the retry logic.
}
}
});
RTSストリームを再生するときに、プロトコルがサポートされていないことを示すエラーメッセージが返された場合はどうすればよいですか?
問題をトラブルシューティングするには、次の手順を実行します。
AliyunPlayer、AlivcArtc、およびRtsの依存関係が追加されているかどうかを確認します。
ApsaraVideo Playerを使用してリアルタイムストリーミング (Rts) ストリームを再生する前に、AliyunPlayer、AlivcArtc、およびRTSの依存関係を追加する必要があります。 詳細については、「Native RTS SDK For Android」をご参照ください。
RTS SDKとApsaraVideo Player SDKのバージョンが一致するかどうかを確認します。
RTS SDKでサポートされているバージョンのApsaraVideo Player SDKを使用する必要があります。 詳しくは、「SDK のダウンロード」をご参照ください。
AliyunPlayerとAlivcArtcのバージョンが同じかどうかを確認します。 AliyunPlayerとAlivcArtcには同じバージョンを使用する必要があります。
統合後、RTS SDKを手動で読み込む必要があります。
load RtsSDK:System.loadLibrary("RtsSDK");
オプション: エラーが解決しない場合は、AndroidManifest.xmlファイルで
android:extractNativeLibs="true"
を設定し、app/build.gradleファイルでandroid{}
を設定します。 サンプルコード:packagingOptions { jniLibs { useLegacyPackaging true } }
サムネイル機能を使用するにはどうすればよいですか?
ApsaraVideo Player SDKのサムネイル機能を使用する前に、次の操作を実行してビデオのサムネイルを生成してください。 ApsaraVideo VODコンソールに移動し、スナップショットテンプレートを作成し、スナップショットタイプとしてImage Spriteを選択します。 スナップショットテンプレートに基づいてビデオを処理するワークフローを作成します。 次に、ビデオのスプライトスナップショットが作成されます。 詳細については、「ビデオスナップショット」をご参照ください。 次のサンプルコードは、ApsaraVideo Player SDKのサムネイル機能を使用する方法の例を示しています。
mAliPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() {
@Override
public void onPrepared() {
// 1. Obtain the URL of the thumbnail.
List<Thumbnail> thumbnailList = mAliPlayer.getMediaInfo().getThumbnailList();
// 2. Create the ThumbnailHelper class.
ThumbnailHelper mThumbnailHelper = new ThumbnailHelper(thumbnailList.get(0).mURL);
// 3. Configure the listener.
mThumbnailHelper.setOnPrepareListener(new ThumbnailHelper.OnPrepareListener() {
@Override
public void onPrepareSuccess() {
// 5. After the thumbnail is loaded, you can request to obtain the thumbnail at a specified playback position.
mThumbnailHelper.requestBitmapAtPosition(1000);
}
@Override
public void onPrepareFail() {}
});
mThumbnailHelper.setOnThumbnailGetListener(new ThumbnailHelper.OnThumbnailGetListener() {
@Override
public void onThumbnailGetSuccess(long positionMs, ThumbnailBitmapInfo thumbnailBitmapInfo) {
// 6. Obtain the thumbnail bitmap at a specified playback position.
Bitmap thumbnailBitmap = thumbnailBitmapInfo.getThumbnailBitmap();
}
@Override
public void onThumbnailGetFail(long positionMs, String errorMsg) {}
});
// 4. Load the thumbnail.
mThumbnailHelper.prepare();
}
});
注: ビデオの再生にURLを使用すると、プレーヤーがThumbnailListパラメーターを取得できない場合があります。 サムネイルのURLを指定することを推奨します。
mAliPlayer.setOnPreparedListener(new IPlayer.OnPreparedListener() {
@Override
public void onPrepared() {
// 1. Create the ThumbnailHelper class.
ThumbnailHelper mThumbnailHelper = new ThumbnailHelper(URL);
//2. Configure a listener.
mThumbnailHelper.setOnPrepareListener(new ThumbnailHelper.OnPrepareListener() {
@Override
public void onPrepareSuccess() {
// 4. After the thumbnail is loaded, you can request to obtain the thumbnail at a specified playback position.
mThumbnailHelper.requestBitmapAtPosition(1000);
}
@Override
public void onPrepareFail() {}
});
mThumbnailHelper.setOnThumbnailGetListener(new ThumbnailHelper.OnThumbnailGetListener() {
@Override
public void onThumbnailGetSuccess(long positionMs, ThumbnailBitmapInfo thumbnailBitmapInfo) {
// 5. Obtain the thumbnail bitmap at a specified playback position.
Bitmap thumbnailBitmap = thumbnailBitmapInfo.getThumbnailBitmap();
}
@Override
public void onThumbnailGetFail(long positionMs, String errorMsg) {}
});
// 3. Load the thumbnail.
mThumbnailHelper.prepare();
}
});
ビデオ解像度に関するFAQ
ビデオが複数の解像度にトランスコードされている場合、どの解像度がビデオ再生に使用されますか?
デフォルトでは、解像度はFD、LD、SD、HD、2K、4K、ODの順序で使用されます。 ビデオ解像度の詳細については、「メディアアセットのパラメーター」をご参照ください。 ApsaraVideo Player SDKは、ビデオの再生に前述の順序で使用できる最初の解像度を使用します。
ビデオ再生のデフォルト解像度を設定するにはどうすればよいですか?
次のサンプルコードは、デフォルトの解像度を設定する方法の例を示しています。
// Video playback based on VidSts is used in this example.
VidSts vidSts = new VidSts();
// For information about the code used for setting parameters such as vid, AccessKeyId, AccessKeySecret, and token, see the documentation about creating an ApsaraVideo player.
/*
Parameter 1: The resolution in which you want to play videos. Valid values: FD, LD, SD, HD, 2K, 4K, and OD.
Parameter 2: Specifies whether to forcibly use the specified resolution to play videos. false: ApsaraVideo Player SDK uses the first resolution that is available in the preceding order for video playback. true: ApsaraVideo Player SDK forcibly uses the specified resolution to play videos. If a video in the specified resolution is not available, the video is not played.
*/
vidSts.setQuality("",false);
同じ解像度の複数のストリームが利用可能な場合、どのストリームが再生されますか?
ApsaraVideo Player SDKは、同じ解像度の複数のストリームが利用可能な場合、トランスコードされた最新のストリームを再生します。
ダウンロードしたビデオに表示されるが、ビデオの再生中に非表示になるように透かしを設定するにはどうすればよいですか?
ビデオを複数の解像度にトランスコードするトランスコードジョブを作成します。 ビデオのウォーターマーキングを解像度で設定し、この解像度をビデオの再生に使用します。 ビデオのダウンロードには他の解像度を使用します。
エラーログを取得するにはどうすればよいですか?
Alibaba Cloudテクニカルサポートに連絡するときは、トラブルシューティングを容易にするためにエラーログを提供してください。 エラーログを取得するには、次の手順を実行します。
エラーログを取得します。
エラーログを取得する前に、ログレベルを
AF_LOG_LEVEL_TRACE
に設定することを推奨します。 詳細については、「SDKログの取得」をご参照ください。トラブルシューティングのために生成されたログをAlibaba Cloudテクニカルサポートに提供します。