changeset 120:8e438d2a1529

add tiny lipsync code.
author Naoya OYAMA <naoya.oyama@gmail.com>
date Sun, 25 Apr 2010 18:26:32 +0900
parents 4e7aaa72e158
children e915d31c5bd9
files recpt1/tssplitter_lite.c recpt1/tssplitter_lite.h
diffstat 2 files changed, 75 insertions(+), 180 deletions(-) [+]
line wrap: on
line diff
--- 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;
--- 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;