昨日の続き。
今日はDownload()の詳細を見ていく。
Download()
Download()で行ってるのは、初期設定を行ったあと、データを取得してファイルに書き込み、進捗度合いを計算するくらい。
一応、流れを書いておくと、
- 終了判定(回避策)
- RTMP_READの設定
- データの取得、書き込み
- バッファ時間の更新
- 進捗度合いの計算
- 繰り返しのチェック
という感じ。
1. 終了判定(回避策)
データの取得を行う前に、動画全体の長さと最後のキーフレームのタイムスタンプがある場合には、終了判定を行っている。
これは、一番最後までデータを取得した状態でさらにデータを読もうとしたときに、何か問題があるらしく、その回避策とのこと。
具体的には、最後のキーフレームのタイムスタンプが動画全体の長さの99.9%以上になっていたら、終了と判定している。
なお、動画全体の長さの単位は秒で、キーフレームのタイムスタンプの単位はミリ秒なので、注意が必要。
2. RTMP_READの設定
RTMP_READは構造体RTMPの中に含まれる構造体で、データを取得する際の状態を保持するための構造体みたい。
/* RTMP_READのメンバflagsで立てれるビット */ #define RTMP_READ_HEADER 0x01 #define RTMP_READ_RESUME 0x02 #define RTMP_READ_NO_IGNORE 0x04 #define RTMP_READ_GOTKF 0x08 #define RTMP_READ_GOTFLVK 0x10 #define RTMP_READ_SEEKING 0x20 /* RTMP_READのメンバstatusの値として取りうる値 */ #define RTMP_READ_COMPLETE -3 #define RTMP_READ_ERROR -2 #define RTMP_READ_EOF -1 #define RTMP_READ_IGNORE 0 typedef struct RTMP_READ { char *buf; char *bufpos; unsigned int buflen; uint32_t timestamp; uint8_t dataType; uint8_t flags; int8_t status; // 以下は、再開するときに設定する uint8_t initialFrameType; uint32_t nResumeTS; char *metaHeader; char *initialFrame; uint32_t nMetaHeaderSize; uint32_t nInitialFrameSize; uint32_t nIgnoredFrameCounter; uint32_t nIgnoredFlvFrameCounter; } RTMP_READ; typedef struct RTMP { // (省略) RTMP_READ m_read; // (省略) } RTMP;
ダウンロードを途中から再開する場合には、以下のようにする。
- メンバflagsにRTMP_READ_RESUMEのフラグを立てる。
- メンバtimestampに最後のキーフレームのタイムスタンプを設定。
- メンバinitialFrameTypeに最後のキーフレームのタイプを設定。
- メンバnResumeTSに最後のキーフレームのタイムスタンプを設定。
- メンバmetaHeaderにファイルのメタデータを設定。
- メンバinitialFrameに最後のキーフレームのデータを設定。
- メンバnMetaHeaderSizeにメタデータのサイズを設定。
- メンバnInitialFrameSizeに最後のキーフレームのサイズを設定。
3. データの取得、書き込み
バッファを用意して、RTMP_Read()でデータをバッファに読み込む。
なお、バッファのサイズは64KB(64 * 1024)としている。
RTMP_Read()のインタフェースは、以下。
int // 取得したデータのサイズ RTMP_Read( RTMP *r, // [IN ] RTMPオブジェクト char *buf, // [OUT] バッファ int size); // [IN ] バッファのサイズ
データを取得したら、fwrite()でファイルに書き込んでいる。
そのあと、動画全体の長さを取得できていない場合には、RTMP_GetDuration()で動画全体の長さを取得している。
double RTMP_GetDuration( RTMP *r); // [IN ] RTMPオブジェクト
なお、読み込んだデータのサイズが0以下の場合には、RTMP_READのメンバstatusをチェック。
もしRTMP_READ_EOFならデータの終端に達したので、ループを抜ける。
4. バッファ時間の更新
動画全体の時間が取得できている場合、必要ならバッファ時間の更新を行う。
バッファ時間が動画全体の時間より短い場合、(動画全体の時間(秒) * 1000 + 5000)(ミリ秒)を新たなバッファ時間として、RTMP_SetBufferMS()、RTMP_UpdateBufferMS()で更新している。
void RTMP_UpdateBufferMS( RTMP *r); // [IN ] RTMPオブジェクト
5. 進捗度合いの計算
現在のタイムスタンプ(RTMP_READのメンバtimestampに入っている)と動画全体の時間から、進捗度合いの計算を行い、必要なら進捗具合を表示している。
6. 繰り返しのチェック
エラーが発生したり、接続が切れてしまったり、タイムアウトになっていない限り、3.に戻って、再びデータの取得と書き込みを行なっていく。
次回予告?
これでrtmpdumpのコードは読めたので、次はRuby-FFIを使ってlibrtmpをRubyから使えるようにし、ニコニコ動画のダウンロードツールでRTMPの動画のダウンロードの進捗度合いを表示できるようにする予定。
今日はここまで!