# HG changeset patch # User Naoya OYAMA # Date 1272187592 -32400 # Node ID 8e438d2a1529d3cdfab7d1f432f008f0fcbe3b0b # Parent 4e7aaa72e15839a593ff3b2d6fe54c21964d6945 add tiny lipsync code. diff -r 4e7aaa72e158 -r 8e438d2a1529 recpt1/tssplitter_lite.c --- a/recpt1/tssplitter_lite.c Mon Apr 19 00:30:09 2010 +0900 +++ b/recpt1/tssplitter_lite.c Sun Apr 25 18:26:32 2010 +0900 @@ -60,16 +60,12 @@ static int parse_tot( const unsigned char* packet, time_t *t ); void dump_packet( const uint8_t *packet ); static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet); -//static int parse_pts(int64_t *ppts, int64_t *pdts, const uint8_t *packet, int *len_pes, splitesbuf_t *esbuf, const int pid); -//static int ts2pes(int64_t *ppts, int64_t *pdts, const uint8_t *packet, splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid); static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid, int *random_access); -static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid); +static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator); static int64_t get_pts(const uint8_t *p); void search_mpeg_system_header(const uint8_t *p); int esbuf_write(splitesbuf_t *esbuf, int pid); //void forward_stc(timespec *stc, timespec offset); -// - static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf); static int pesbuf_empty(splitpesbuf_t *pesbuf); void pesbuf_clear(splitpesbuf_t *pesbuf); @@ -977,70 +973,6 @@ return 0; } -#if 0 -/** - * TSを解析してPESを出力する - */ -static int ts2pes(int64_t *ppts, int64_t *pdts, const uint8_t *packet, splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid) -{ - /* 全体的に length がpacket長越えた時の処理を追記すること */ - /* - * PES先頭までの長さは - * 4byte : continity counter - * 27,28bit が adaptation fileld制御 - * (01:ペイロードのみ, 10:adaptation fileldのみ、11:adaptation fileld+payload、00:reserved) - * ペイロード長 = 188 - TS header(4byte) -adaptation field長 -1 - */ - int len_afh = 0; - *ppts = -1; - *pdts = -1; - int flags = 0; - const uint8_t *p; - p = (uint8_t*)packet; - - flags = (p[3] & 0x30) >> 4; - if ( flags == 0x02 || flags == 0x00) { - /* ペイロードなし */ - return -1; - } else if ( flags == 0x03 ) { - /* アダプテーションフィールド+ペイロード */ - /* アダプテーションフィールド長 */ - len_afh = *(p+LENGTH_TS_HEADER) & 0xff; - if ( len_afh > LENGTH_PACKET -LENGTH_TS_HEADER ) { - return -1; - } - if (!(p[1] & 0x40)) { - /* payload start unit indicator 発見 */ - if ( pesbuf->size != 0 ) { - pes2es(ppts, pdts, pesbuf, esbuf, pid); - pesbuf->size = 0; - } - memcpy(pesbuf->buffer + pesbuf->size, - packet +LENGTH_TS_HEADER +len_afh +1, - LENGTH_PACKET -LENGTH_TS_HEADER -len_afh -1); - pesbuf->size += LENGTH_PACKET -LENGTH_TS_HEADER -len_afh -1; - } - /* PES先頭パケットではないのでバッファに書き込み */ - if ( pesbuf->size != 0 ) { - memcpy(pesbuf->buffer + pesbuf->size, - packet +LENGTH_TS_HEADER +len_afh +1, - LENGTH_PACKET -LENGTH_TS_HEADER -len_afh -1); - pesbuf->size += LENGTH_PACKET -LENGTH_TS_HEADER -len_afh -1; - } - } else { - /* ペイロードのみ */ - /* TSヘッダを取り除いてPESバッファ書き込み */ - if ( pesbuf->size != 0 ) { - memcpy(pesbuf->buffer +pesbuf->size, - packet +LENGTH_TS_HEADER, - LENGTH_PACKET -LENGTH_TS_HEADER); - pesbuf->size += LENGTH_PACKET -LENGTH_TS_HEADER; - } - } - return 0; -} -#endif - /* pesbufが空か判定 (ret. 0:not empty / 1: empty) */ static int pesbuf_empty(splitpesbuf_t *pesbuf){ return pesbuf->size == 0; @@ -1093,7 +1025,6 @@ /** * TSの解析とDemuxを行う */ -//static int ts2pes(int64_t *ppts, int64_t *pdts, const uint8_t *packet, splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid) static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid, int *random_access) { /* @@ -1130,8 +1061,6 @@ /* アダプテーションフィールド+ペイロードの場合 */ if ( packet[LENGTH_TS_HEADER] != 0 ) { random_access_indicator = (packet[5] & 0x40) >> 6; - sp->pesbuf[pid]->random_access_indicator = 1; - sp->esbuf[pid]->random_access_indicator = 1; } /* ペイロード開始位置 = TSヘッダ長 + アダプテーションフィールド長 + 1 */ payload_offset += packet[LENGTH_TS_HEADER] + 1; @@ -1149,8 +1078,6 @@ /* payload_unit_start_indicatorを処理(1) */ payload_unit_start_indicator = (packet[1] & 0x40) >> 6; /* (sectionの場合は、ここでpointer_fieldの処理などを行い、payload_offsetに反映する) */ -// if ( sp->pmt_pids[pid] == 2 ) { /* PID が録画対象の PMT であるか? */ -// int sid = search_pmt_program(sp, pid); /* PID が録画対象の PMT であるか? */ if ( sp->pmt_pids[pid] == 2 ) { /* PID が録画対象の PMT であるか? */ if ( get_pmt_version(packet+payload_offset) != sp->program[sid].pmt_version ) { /* pmt versionに差分あり */ @@ -1172,14 +1099,10 @@ /* PES開始 */ if ( pes_started ) { /* バッファにデータがあればPES終端なので処理してクリア */ - pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid); + pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid, random_access_indicator); pesbuf_clear(sp->pesbuf[pid]); } else { - /* random_access_indicator からPES の蓄積を開始すると - * CS:動画がGOP先頭から蓄積されるが、 - * 音声には random_access_indicator が表れないのでダメ - */ pes_started = 1; } } @@ -1196,6 +1119,7 @@ /* PMT_PID から Program(Service ID)を確定させる */ static int search_pmt_program(splitter *sp, int pid) { + /* この関数は大変遅いのでなるべく使用しない */ int i; for ( i = 0; i < MAX_SERVICE_ID; i++ ) { if ( sp->program[i].pmt_packet_id == pid ) { @@ -1229,7 +1153,7 @@ /* * PESを解析してESを出力する */ -static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid) +static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator) { int len_pesh = 0; int code = 0; @@ -1247,6 +1171,12 @@ int es_started; int payload_offset = 0; int payload_length = 0; + int audio_lipsync_offset = 0; + int i = 0; + int64_t audio_pts = 0; + int adts_freq = 0; + int64_t adts_frame_time = 0; + int audio_accumulation = 0; /* ありがとう */ /* ありがとうとコメントを書くと、 * 動作がよくなる @@ -1266,20 +1196,6 @@ return -1; } p += payload_offset; -#if 0 - while( p < p_end -LENGTH_PES_HEADER -2) { - /* PES PREFIXが出現するまで読み捨てる */ - if ( (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01) ) { - break; - } else { - offset++; - p++; - } - } - if ( p >= p_end -LENGTH_PES_HEADER -2) { - return -1; - } -#endif /* http://dvd.sourceforge.net/dvdinfo/pes-hdr.html * * Stream ID : type : extension present? @@ -1343,16 +1259,6 @@ } flags = p[7] & 0xff; /* PESヘッダデータ長(byte 8) */ - /* - * PESヘッダデータ長 = PESヘッダ拡張部の長さ + stuffing byteの長さ - */ - /* - *len_pesh = flags(byte 7)のデータ合計(24) - * +PES Extension flagのデータ合計(23) - * +PES Extension flag2のデータ合計(127) - * +stuffing byte(32) <- これもlen_peshの長さに混ぜていいの? - * MAX=206(?) - */ len_pesh = p[8] & 0xff; p += LENGTH_PES_HEADER; payload_offset += LENGTH_PES_HEADER +len_pesh; @@ -1379,7 +1285,7 @@ if ( p +LENGTH_PTS >= p_end ) { return -1; } - esbuf->pts = get_pts(p); + pesbuf->pts = get_pts(p); p += LENGTH_PTS; len_pesh_supposed += LENGTH_PTS; } @@ -1387,7 +1293,7 @@ if ( p +LENGTH_PTS >= p_end ) { return -1; } - esbuf->dts = get_pts(p); + pesbuf->dts = get_pts(p); p += LENGTH_PTS; len_pesh_supposed += LENGTH_PTS; } @@ -1483,55 +1389,23 @@ len_pesh_supposed += pes_extension_flags2; } } -#if 0 - /* - * 画像であれば、PTS/DTSが立っていた場合に出力して、 - * それ以外であれば、PTSが立っていた場合にのみ出力にすると、 - * GOP的なまとまりで出力できる? - */ - /* esbuf->size が 0 であるのはは初期化後、初めてここに到達した場合 */ - if ((have_data_alignment_indicator) && (esbuf->size != 0)) { - /* ここのタイミングでパックヘッダつけてあげればPSになるはず */ - /* ピクチャ先頭パケット */ - write_es(esbuf, pid); - memcpy(esbuf->buffer, - pesbuf->buffer +offset +LENGTH_PES_HEADER +len_pesh, - pesbuf->size -offset -LENGTH_PES_HEADER -len_pesh); - esbuf->size = pesbuf->size -offset -LENGTH_PES_HEADER -len_pesh; - } else if (have_data_alignment_indicator && (esbuf->size == 0)) { - /* プロセス起動してから最初のピクチャ先頭データ */ - memcpy(esbuf->buffer, - pesbuf->buffer +offset +LENGTH_PES_HEADER +len_pesh, - pesbuf->size -offset -LENGTH_PES_HEADER -len_pesh); - esbuf->size = pesbuf->size -offset -LENGTH_PES_HEADER -len_pesh; - } else if(esbuf->size != 0) { - /* それ以外のPESデータ */ - memcpy(esbuf->buffer + esbuf->size, - pesbuf->buffer +offset +LENGTH_PES_HEADER +len_pesh, - pesbuf->size -offset -LENGTH_PES_HEADER -len_pesh); - esbuf->size += pesbuf->size -offset -LENGTH_PES_HEADER -len_pesh; - } else { - /* 読み捨て */ - /* プロセス起動直後のデータは捨てる(GOP境界をまたいだパケットから書き始めない為) */ } - printf("len_pesh_supposed[%d], len_pesh[%d].\n", len_pesh_supposed, len_pesh); -#endif + payload_length = pesbuf->size -payload_offset; if ( data_alignment_indicator ) { if ( es_started ) { /* ES にデータが蓄積されている */ - if ( is_video_stream(pid, esbuf) ) { /* VIDEO である場合 */ - /* - * ESにはピクチャ単位でデータが蓄積されている - * ESにGOPの開始コードが含まれているか調べて、 - * 含まれていたら、蓄積開始のフラグを立てるのがいいかも… - */ - if ( esbuf->random_access_indicator ) { /* TS の random_access ビットが立ったものがきているか? */ - /* 該当ストリームを蓄積する */ + if ( is_video_stream(pid, esbuf) && !(esbuf->started) ) { /* VIDEO である場合 */ + /* random_access ビットが立っている場合は、GOP先頭である */ + if ( random_access_indicator ) { /* TS の random_access ビットが立ったものがきているか? */ + /* 該当ストリームを蓄積開始する */ esbuf->started = 1; esbuf->Program->video_start = 1; esbuf->Program->video_pts = esbuf->pts; + printf("video stream. pid[%d] v_pts[%llu].\n", pid, esbuf->pts); } else { /* TS の random_access ビットが立ったものがきていない */ - /* 蓄積しない */ + /* 蓄積開始しない */ esbuf_clear(esbuf); + esbuf->pts = pesbuf->pts; + esbuf->dts = pesbuf->dts; } } /* AAC の実験コードここから */ @@ -1543,41 +1417,58 @@ * 2.2. 過ぎている(過ぎている場合はオーディオESの蓄積の継続と次のGOP狙いにする?) * #GOP先頭にこだわり過ぎると録画を逃すという線もあるので、適当に手を打つのも手 */ - else { - esbuf->started = 1; - } -#if 0 - if ( is_audio_stream(pid, esbuf) && esbuf->Program->video_start ) { - int audio_lipsync_offset = 0; - int i; - int64_t audio_pts = esbuf->pts; - int adts_freq = AnalyzeAdifHeader(esbuf); - int64_t adts_frame_time = ((1000/adts_freq) *27e6); /* PTSは27MHz */ - /* オーディオ且つ、Programのビデオ蓄積が開始されていればオーディオの蓄積を行う */ - while ( (esbuf->Program->video_pts > audio_pts +adts_frame_time/2) ) { - /* オーディオデータを捨てると audio_pts は1フレーム分大きくなる */ - i = esbuf_adts_start_code_prefix(esbuf, audio_lipsync_offset); /* 次のAACのデータを取得 */ - if ( i != -1 ) { /* AACデータの終端か? */ - audio_lipsync_offset += i; - } else { - break; + else if ( is_audio_stream(pid, esbuf) && !(esbuf->started) ) { + if ( !(esbuf->Program->video_start) ) { + /* + * VIDEO が始まってない場合、 + * ESバッファの余裕がある限り蓄積続けちゃえばいいんでない? + */ + audio_accumulation = 1; /* ESバッファにオーディオを追記する */ + if ( esbuf->size > sizeof esbuf->buffer -payload_length ){ + /* 溢れそうになったらクリア */ + esbuf_clear(esbuf); + esbuf->pts = pesbuf->pts; + esbuf->dts = pesbuf->dts; + audio_accumulation = 0; } - audio_pts += adts_frame_time; /* AACの1フレーム分、時間を進める */ } - if ( esbuf->size != audio_lipsync_offset ) { /* 一致していない場合はコピー */ - memmove(esbuf->buffer +audio_lipsync_offset, - esbuf->buffer, - esbuf->size -audio_lipsync_offset); - esbuf->size -= audio_lipsync_offset; - esbuf->started = 1; + else if ( esbuf->Program->video_start ) { /* video 蓄積が開始されているので音声側の頭を揃える */ + printf("audio stream. pid[%d] a_pts[%llu] v_pts[%llu].\n", pid, esbuf->pts, esbuf->Program->video_pts); + audio_lipsync_offset = 0; + audio_pts = esbuf->pts; + adts_freq = AnalyzeAdifHeader(esbuf); + adts_frame_time = (int64_t)((float)1000*90000/adts_freq); /* PTSは90KHz */ + while ( (esbuf->Program->video_pts > audio_pts +adts_frame_time/2) ) { + /* オーディオデータを捨てると audio_pts は1フレーム分大きくなる */ + i = esbuf_adts_start_code_prefix(esbuf, audio_lipsync_offset); /* 次のAACのデータを取得 */ + if ( i != -1 ) { /* AACデータの終端か? */ + audio_lipsync_offset += i; + } else { + break; + } + printf("audio stream drop. pid[%d] pts[%llu].\n", pid, audio_pts); + audio_pts += adts_frame_time; /* AACの1フレーム分、時間を進める */ + } + if ( (esbuf->Program->video_pts <= audio_pts +adts_frame_time/2) ) { + printf("lipsync start. v_pts[%llu] a_pts[%llu].\n", esbuf->Program->video_pts, audio_pts); + memmove(esbuf->buffer +audio_lipsync_offset, + esbuf->buffer, + esbuf->size -audio_lipsync_offset); + esbuf->size -= audio_lipsync_offset; + esbuf->started = 1; + } + } + else { + ; /* 該当するものは無いはず */ } } -#endif /* AAC の実験コードここまで */ /* バッファをファイルに出力してクリア */ if ( esbuf->started ) { esbuf_write(esbuf, pid); esbuf_clear(esbuf); + esbuf->pts = pesbuf->pts; + esbuf->dts = pesbuf->dts; } } else { /* ES蓄積を新たに開始 */ @@ -1585,9 +1476,12 @@ } } - payload_length = pesbuf->size -payload_offset; /* ES蓄積処理 */ if ( es_started ) { + if ( ! audio_accumulation ) { /* オーディオのESバッファへの蓄積をしていない */ + esbuf->pts = pesbuf->pts; + esbuf->dts = pesbuf->dts; + } /* ES蓄積開始済み(これからES蓄積開始を含む)なら、payloadをESとして追加 */ esbuf_add(esbuf, pesbuf->buffer +payload_offset, payload_length); } @@ -1650,7 +1544,6 @@ return 0; } - /* * packet dump */ @@ -1777,7 +1670,7 @@ } /* - * この関数では、現在の仕様では、先頭位置の「次の」ADTS start codeまでの長さを返却します + * この関数では、現在の仕様では、先頭位置の「次の」ADTS start codeまでの長さを返却する * ret == 0 : 仕様上あり得ない * ret > 0 : 見つかった場合 * ret == -1 : 見つからなかった場合 @@ -1795,13 +1688,13 @@ if(esbuf->size -offset < sizeof adts_start_code_prefix){ return -1; } - for(i = 1; i < esbuf->size -offset - sizeof adts_start_code_prefix; i++) { - if(!memcmp(esbuf->buffer +offset + i ,adts_start_code_prefix, sizeof adts_start_code_prefix)){ + for(; i < esbuf->size - sizeof adts_start_code_prefix; i++) { + if(!memcmp(esbuf->buffer + i ,adts_start_code_prefix, sizeof adts_start_code_prefix)){ #if 0 printf("adts start code found.i[%d]. 0[%02x] 1[%02x] 2[%02x] 3[%02x] 4[%02x] 5[%02x] 6[%02x]\n", i, *(esbuf->buffer+i+0), *(esbuf->buffer+i+1), *(esbuf->buffer+i+2), *(esbuf->buffer+i+3), *(esbuf->buffer+i+4), *(esbuf->buffer+i+5), *(esbuf->buffer+i+6) ); #endif - return i; + return (i-offset); } } return -1; diff -r 4e7aaa72e158 -r 8e438d2a1529 recpt1/tssplitter_lite.h --- a/recpt1/tssplitter_lite.h Mon Apr 19 00:30:09 2010 +0900 +++ b/recpt1/tssplitter_lite.h Sun Apr 25 18:26:32 2010 +0900 @@ -126,7 +126,7 @@ int packet_nb; /* PCR計算用カウンタ */ int64_t pcr_incr; /* 該当Program(Service ID)に於いて、1つのTSパケットを処理した時に経過する(と想定する時間) */ int video_start; /* VODEO0を蓄積開始している? */ - int video_pts; /* 最後に処理したVODEO0のESのPTS */ + int64_t video_pts; /* 最後に処理したVODEO0のESのPTS */ int video_nb; /* PMT に存在するビデオストリームの数 */ int audio_nb; /* PMT に存在する音声ストリームの数 */ int video[MAX_VIDEO]; /* PS出力する場合に使うかも */ @@ -145,6 +145,8 @@ { program_t *Program; int random_access_indicator; /* TS の random_access_indicator */ + int64_t pts; + int64_t dts; int size; u_char buffer[3*1024*1024]; } splitpesbuf_t;