Mercurial > pt1.oyama
changeset 122:4009737ea899
add es output arg:
add start_time arg:
author | Naoya OYAMA <naoya.oyama@gmail.com> |
---|---|
date | Wed, 05 May 2010 20:43:43 +0900 |
parents | e915d31c5bd9 |
children | 215a51fa3df3 |
files | recpt1/configure.ac recpt1/recpt1.c recpt1/tssplitter_lite.c recpt1/tssplitter_lite.h |
diffstat | 4 files changed, 539 insertions(+), 301 deletions(-) [+] |
line wrap: on
line diff
--- a/recpt1/configure.ac Thu Apr 29 02:02:42 2010 +0900 +++ b/recpt1/configure.ac Wed May 05 20:43:43 2010 +0900 @@ -18,7 +18,6 @@ # Checks for libraries. AC_CHECK_LIB([m], [log10]) AC_CHECK_LIB([pthread], [pthread_kill]) -AC_CHECK_LIB([rt], [log10]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT
--- a/recpt1/recpt1.c Thu Apr 29 02:02:42 2010 +0900 +++ b/recpt1/recpt1.c Wed May 05 20:43:43 2010 +0900 @@ -1,4 +1,5 @@ /* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +/* vim: set ts=4 sts=4 sw=4 expandtab number : */ #include <stdio.h> #include <fcntl.h> #include <sys/types.h> @@ -336,8 +337,8 @@ if(use_b25) { code = b25_decode(dec, &sbuf, &dbuf); if(code < 0) { - fprintf(stderr, "b25_decode failed. fall back to encrypted recording.\n"); - use_b25 = FALSE; /* local flag */ + fprintf(stderr, "b25_decode failed (code=%d). fall back to encrypted recording.\n", code); + use_b25 = FALSE; } else buf = dbuf; @@ -361,6 +362,12 @@ /* $BJ,N%BP>](BPID$B$,40A4$KCj=P$G$-$k$^$G=PNO$7$J$$(B * 1$BICDxEYM>M5$r8+$k$H$$$$$+$b(B */ + time_t cur_time; + time(&cur_time); + if(cur_time - data->start_time > 4) { + use_splitter = FALSE; + goto fin; + } break; } } @@ -480,9 +487,9 @@ show_usage(char *cmd) { #ifdef HAVE_LIBARIB25 - fprintf(stderr, "Usage: \n%s [--b25 [--round N] [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] channel [--es filename_suffix] rectime destfile\n", cmd); + fprintf(stderr, "Usage: \n%s [--b25 [--round N] [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] channel rectime destfile\n", cmd); #else - fprintf(stderr, "Usage: \n%s [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] channel rectime destfile\n", cmd); + fprintf(stderr, "Usage: \n%s [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] channel rectime destfile\n", cmd); #endif fprintf(stderr, "\n"); fprintf(stderr, "Remarks:\n"); @@ -507,6 +514,7 @@ fprintf(stderr, "--lnb voltage: Specify LNB voltage (0, 11, 15)\n"); fprintf(stderr, "--sid SID1,SID2,...: Specify SID number in CSV format (101,102,...)\n"); fprintf(stderr, " --es filename: Specify ES out filename prefix\n"); + fprintf(stderr, " --start_time YYYYMMDDHHMISS: Specify record start datetime\n"); fprintf(stderr, "--help: Show this help\n"); fprintf(stderr, "--version: Show version\n"); fprintf(stderr, "--list: Show channel list\n"); @@ -896,6 +904,7 @@ { "SID", 1, NULL, 'i'}, { "es", 1, NULL, 'e'}, { "ES", 1, NULL, 'e'}, + { "start_time", 1, NULL, 'y'}, {0, 0, NULL, 0} /* terminate */ }; @@ -912,6 +921,7 @@ char *voltage[] = {"0V", "11V", "15V"}; char *sid_list = NULL; char *es_name_prefix = NULL; + char *start_time = NULL; while((result = getopt_long(argc, argv, "br:smn:ua:p:d:hvli:", long_options, &option_index)) != -1) { @@ -992,6 +1002,9 @@ case 'e': es_name_prefix = optarg; break; + case 'y': + start_time = optarg; + break; } } @@ -1049,15 +1062,13 @@ if(!dec) { fprintf(stderr, "Cannot start b25 decoder\n"); fprintf(stderr, "Fall back to encrypted recording\n"); - use_b25 = 0; + use_b25 = FALSE; } } /* initialize splitter */ - if(use_splitter) - { - splitter = split_startup(sid_list, es_name_prefix); - if ( splitter->sid_list == NULL ) - { + if(use_splitter) { + splitter = split_startup(sid_list, es_name_prefix, start_time); + if(splitter->sid_list == NULL) { fprintf(stderr, "Cannot start TS splitter\n"); return 1; }
--- a/recpt1/tssplitter_lite.c Thu Apr 29 02:02:42 2010 +0900 +++ b/recpt1/tssplitter_lite.c Wed May 05 20:43:43 2010 +0900 @@ -48,6 +48,9 @@ #define AV_RB16(x) ((((const uint8_t*)(x))[0] << 8) | ((const uint8_t*)(x))[1]) #endif #define MAX_SERVICE_ID ( 0xffff ) +#define LIST_DECIMAL "0123456789" +#define TSS_STREAM_TYPE_AUDIO (1) +#define TSS_STREAM_TYPE_VIDEO (2) /* prototypes */ static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); @@ -58,23 +61,23 @@ static int GetCrc32(unsigned char *data, int len); static int GetPid(unsigned char *data); static int parse_tot( const unsigned char* packet, time_t *t ); -void dump_packet( const uint8_t *packet ); +//void dump_packet( const uint8_t *packet ); static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet); -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, int random_access_indicator); +static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid); +//static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator); +static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid); static int64_t get_pts(const uint8_t *p); -void search_mpeg_system_header(const uint8_t *p); -int esbuf_write(splitesbuf_t *esbuf); -//void forward_stc(timespec *stc, timespec offset); +//void search_mpeg_system_header(const uint8_t *p); +static int esbuf_write(splitesbuf_t *esbuf); static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf); static int pesbuf_empty(splitpesbuf_t *pesbuf); void pesbuf_clear(splitpesbuf_t *pesbuf); static int pesbuf_add(splitpesbuf_t *pesbuf, const uint8_t *data, int len); static int esbuf_empty(splitesbuf_t *esbuf); -void esbuf_clear(splitesbuf_t *esbuf); +void esbuf_clear(splitesbuf_t *esbuf, uint64_t pts, uint64_t dts); static int esbuf_add(splitesbuf_t *esbuf, const uint8_t *data, int len); static int get_pmt_version(const uint8_t *p); -static int esbuf_adts_start_code_prefix(splitesbuf_t *esbuf, int offset); +static int next_adts_start_code(splitesbuf_t *esbuf, int offset); static int is_video_stream(const int pid, splitesbuf_t *esbuf); static int is_audio_stream(const int pid, splitesbuf_t *esbuf); static int AnalyzeAdifHeader(splitesbuf_t *esbuf); @@ -92,10 +95,11 @@ static int get_adif_aac_frame_length(uint8_t *p); static int get_adts_buffer_fullness(uint8_t *p); static int get_adts_no_raw_data_blocks_in_frame(uint8_t *p); -static int search_pmt_program(splitter *sp, int pid); +//static int search_pmt_program(splitter *sp, int pid); static int search_gop_start_code(splitesbuf_t *esbuf); -//static int creat_filename(char *base, char *filename, int sid, int epid, int av_flag ,splitesbuf_t *esbuf); -static int creat_filename(splitter *sp, int sid, int pid, int av_flag); +static int creat_es_file(splitter *sp, int sid, int pid, int av_flag); +static time_t cue2time(char *yyyymmddhhmiss); +static int search_pcr_pid(splitter *sp, int pid); /** * サービスID解析 @@ -196,7 +200,8 @@ */ splitter* split_startup( char *sid, // [in] サービスID(引数で指定した文字列) - char *filename // [in] 出力ESファイル名(引数で指定したファイル名) + char *filename, // [in] 出力ESファイル名(引数で指定したファイル名) + char *arg_cue // [in] 録画開始時刻(引数で指定した文字列 YYYYMMDDHHMISS) ) { splitter* sp; @@ -215,6 +220,9 @@ } memset(sp->pids, 0, sizeof(sp->pids)); memset(sp->pmt_pids, 0, sizeof(sp->pmt_pids)); + memset(sp->cat_pids, 0, sizeof(sp->cat_pids)); + memset(sp->pcr_pids, 0, sizeof(sp->pcr_pids)); + memset(sp->pcr, 0, sizeof(sp->pcr)); sp->sid_list = NULL; sp->pat = NULL; @@ -227,26 +235,28 @@ sp->pat_count = 0xFF; sp->pmt_retain = -1; sp->pmt_counter = 0; -// sp->STC_timespec.tv_sec = 0; -// sp->STC_timespec.tv_nsec = 0; -// sp->tot_offset.tv_sec = 0; -// sp->tot_offset.tv_nsec = 0; - sp->cue_time = -1; - sp->stc = 0; - sp->status = 1; + sp->time_cue = 0; + sp->time_tot = 0; + sp->pcr_nb = 0; memset(sp->esbuf, 0, sizeof(splitesbuf_t *)*MAX_PID); memset(sp->pesbuf, 0, sizeof(splitpesbuf_t *)*MAX_PID); memset(sp->program, 0, sizeof(program_t *)*MAX_SERVICE_ID); for ( i=0; i < MAX_PID; i++ ) { /* pmt_version は (N%32) の値を取るので、0 で初期化してはならない */ sp->program[i].pmt_version = -1; + /* cue は最大値で初期化(CUE <= STCとなると録画開始するため) */ + sp->program[i].cue = INT64_MAX; } memset(sp->pid_sid_table, 0, sizeof(int)*MAX_PID); -// sp->filename = filename; if ( filename != NULL ) { sp->esout = 1; sp->filename = filename; } + if ( arg_cue != NULL ) { + sp->arg_cue = arg_cue; + } else { + sp->arg_cue = "00000000000000"; /* とりあえず最小値 */ + } return sp; } @@ -270,6 +280,7 @@ */ void split_shutdown(splitter* sp) { + int i = 0; if ( sp != NULL ) { if ( sp->pat != NULL ) { @@ -281,6 +292,20 @@ free(sp->sid_list); sp->sid_list = NULL; } + for(i=0; i < MAX_PID; i++) { + if ( sp->esbuf[i] != NULL ) { + if ( sp->esbuf[i]->fd != -1 ) { + close(sp->esbuf[i]->fd); + sp->esbuf[i]->fd = -1; + } + free(sp->esbuf[i]); + sp->esbuf[i] = NULL; + } + if ( sp->pesbuf[i] != NULL ) { + free(sp->pesbuf[i]); + sp->pesbuf[i] = NULL; + } + } free(sp); sp = NULL; } @@ -294,13 +319,8 @@ static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf) { #if 0 - unsigned char **pat, // [out] PAT 情報(再構築後) - unsigned char* pids, // [out] 出力対象 PID 情報 - char** sid_list, // [in] 出力対象サービス ID のリスト - unsigned char* pmt_pids, // [in] 出力対象PIDのPMT PID - , // [in] pt1_drvの入力TS - int* pmt_retain, // [in] PMTの落とすべき数 - int* pmt_counter // [out] PMTの落とした数 + splitter *sp, // [in/out] splitter構造体 + ARIB_STD_B25_BUFFER *sbuf, // [in] pt1_drvの入力TS #endif int length = sbuf->size; @@ -313,7 +333,6 @@ pid = GetPid(sbuf->data + index + 1); // PAT if(PAT == pid) { - dump_packet(sbuf->data + index); result = AnalyzePat(sp, sbuf->data + index); if(TSS_SUCCESS != result) { /* 下位の関数内部でmalloc error発生 */ @@ -325,20 +344,12 @@ /* 残すpmt_pidである場合には、pmtに書かれている * 残すべきPCR/AUDIO/VIDEO PIDを取得する */ if(sp->pmt_pids[pid] == 1) { - /* - * program(番組)とServiceID をベースに管理することにしているので、 - * pmt_pidsとかもう必要ないかも… - */ /* この中にはPMT毎に一度しか入らないようにしておく */ - int random_access; - //AnalyzePmt(sp, sbuf->data + index, pid); - //AnalyzePmt(sp, sbuf->data + index +4, pid, LENGTH_PACKET-4); sp->pmt_pids[pid]++; sp->pmt_counter += 1; - DemuxTs(sbuf->data +index, sp, pid, &random_access); /* AnalyzePmt より DemuxTs の方がアダプテーションフィールドの処理が良いので変更 */ + DemuxTs(sbuf->data +index, sp, pid); /* AnalyzePmt より DemuxTs の方がアダプテーションフィールドの処理が良いので変更 */ } - /* 録画する全てのPMTについて、中にあるPCR/AUDIO/VIDEOのPIDを - * 得る */ + /* 録画する全てのPMTについて、中にあるPCR/AUDIO/VIDEOのPIDを得る */ /* pmt_counter と pmt_retain が一致する場合に条件は満たされる */ if(sp->pmt_counter == sp->pmt_retain) { result = TSS_SUCCESS; @@ -357,23 +368,22 @@ * TS 分離処理 */ int split_ts( - splitter *splitter, // [in] splitterパラメータ + splitter *sp, // [in] splitterパラメータ ARIB_STD_B25_BUFFER *sbuf, // [in] 入力TS - splitbuf_t *dbuf // [out] 出力TS + splitbuf_t *dbuf // [out] 出力TS ) { int pid; unsigned char *sptr, *dptr; int s_offset = 0; int d_offset = 0; - int64_t pcr; - int64_t pcr_h; - int pcr_l; - struct timespec tot_timespec; - struct timespec local_timespec; - int len_pes; - int random_access; - int sid; + int64_t pcr_h = 0; + int pcr_l = 0; + int ret = 0; + int sid = 0; + program_t *program; + static int packet_nb; /* パケット受信数 */ + int i = 0; /* 初期化 */ dbuf->size = 0; @@ -383,135 +393,162 @@ sptr = sbuf->data; dptr = dbuf->buffer; -#if 0 - /* TOT受信済みであるなら、STC を成長させる */ - if ( sp->tot_offset.tv_nsec != 0 && sp->tot_offset.tv_sec != 0) { - forward_stc(&(sp->STC_timespec), sp->tot_offset); - } -#endif while(sbuf->size > s_offset) { pid = GetPid(sptr + s_offset + 1); - sid = splitter->pid_sid_table[pid]; + sid = sp->pid_sid_table[pid]; /* PIDからSIDを取得 */ switch(pid) { // PAT case PAT: // 巡回カウンタカウントアップ - if(0xFF == splitter->pat_count) { - splitter->pat_count = splitter->pat[3]; + if(0xFF == sp->pat_count) { + sp->pat_count = sp->pat[3]; } else { - splitter->pat_count = (splitter->pat_count + 1) % 16; + sp->pat_count += 1; + if(0 == sp->pat_count % 0x10) { + sp->pat_count -= 0x10; + } } - splitter->pat[3] = splitter->pat_count; + sp->pat[3] = sp->pat_count; - memcpy(dptr + d_offset, splitter->pat, LENGTH_PACKET); + memcpy(dptr + d_offset, sp->pat, LENGTH_PACKET); d_offset += LENGTH_PACKET; dbuf->size += LENGTH_PACKET; break; - case TOT: + case TOT: /* TOT に TDTの情報全てが含まれており、実放送では TOT しか送信されない */ /* TOT は 500msec の誤差が保証されている * 閏秒の場合は最大1.5秒の誤差となる */ -#if 0 - if ( sp->tot_offset.tv_nsec != 0 && sp->tot_offset.tv_sec != 0 ) { - parse_tot(sptr + s_offset, &(tot_timespec.tv_sec)); - clock_gettime(CLOCK_REALTIME, &local_timespec); - - /* TOT をSystem Time Clock(STC)に入れる */ - sp->STC_timespec.tv_sec = tot_timespec.tv_sec; - sp->STC_timespec.tv_nsec = tot_timespec.tv_nsec; - - /* TOT と localtime の差分を sp->tot_offset に入れる */ - sp->tot_offset.tv_sec = tot_timespec.tv_sec - local_timespec.tv_sec; - sp->tot_offset.tv_nsec = tot_timespec.tv_nsec - local_timespec.tv_nsec; - } -#endif - /* STC が既にあるならTOT受信時に頭出し時刻を決定する */ - /* 59秒9990 あたりを頭出し時刻に設定でよいかな */ - if ( splitter->stc != 0 && splitter->cue_time == -1) { - /* TOT から cue_time を計算する */ - /* えいや。で、59秒9990固定決め打ち */ - parse_tot(sptr + s_offset, &(tot_timespec.tv_sec)); - //cue_second = ((tot_timespec.tv_sec % 60) - 59); - splitter->cue_time = splitter->stc + (((59 - (tot_timespec.tv_sec % 60))*(27*1000*1000)) + floor((27*1000*1000)*0.999)); - //printf("stc[%lld]. cue_time[%lld].\n", splitter->stc, splitter->cue_time); - - } else { - /* TOT受診時にSTCが存在しないならTOTは捨てる */ + if ( sp->time_tot == 0 ) { + /* splitter構造体の時刻関係(TOT/CUE)のパラメータ初期化 */ + parse_tot(sptr + s_offset, &(sp->time_tot)); + sp->time_cue = cue2time(sp->arg_cue); + sp->tot_packet_nb = packet_nb; } break; default: - /* 時間管理に関しての実装方針 */ + /* ■時間管理に関しての実装方針■ */ /* - * 時間関係を扱っている変数のまとめ - * PCR : 42Bit @27MHz(プログラム毎に独立) - * PTS : 42Bit @90KHz(SYSTEM ID(ES)毎に独立) - * DTS : 42Bit @90KHz(SYSTEM ID(ES)毎に独立) - * TOT : MJD + 2進化10進数(ストリームに一つだけ) - * STC : 64Bit @27MHz(システムローカル時刻(ソースはTOT)) + * ■時間関係を扱っている変数■ + * PCR : 42Bit @27MHz(ServiceID(ProgramID, sid)毎に独立) + * PTS : 42Bit @90KHz(ES毎に独立) + * DTS : 42Bit @90KHz(ES毎に独立) + * TOT : MJD + 2進化10進数(TSに1つだけ) + * STC : 64Bit @27MHz(ServiceID(ProgramID, sid)の現在時刻(ソースはPCR)) + * CUE : 録画開始時刻 YYYYMMDDHHMISS (引数指定) + * + * ■STCの管理方針■ + * PCR受信時にSTCに時間情報をコピーする + * 1パケットを受信すると経過する時間を加算してSTCを管理する + * (ffmpegのmpegts.cと同じ方針) * - * STC と TOT の関連だけ計算出来るようにしておいて、 - * 出力するようにする/しないの判定は全体的に、 - * STCからTOTを使って出した時刻情報とする。かなぁ。 - * PCR/PTS/DTSはオーバーフローしたときには34Bit目が立っていると見なすこと + * ■CUEとTOTとSTC■ + * TOTからSTCと比較するための情報を作成する。 + * ・TOTは time_t + * ・CUEは time_t + * ・STCは42Bitの27MHz周期の数値 + * TOT 受信時に各Program(ServiceID)のSTCを変換基準値とする + * 録画開始時(27MHz)の値の計算式は、 + * Program->CUE = STCの変換基準値 + 27e6*(ARG_CUE-TOT(秒)) + * 録画の開始確認として Program->CUE と PTS を比較すればよい + * + * ※PCR/PTS/DTSはオーバーフローしたときには34Bit目が立っていると見なすこと※ + * オーバーフロー時の処理は未実装 */ -// if ( 1 == splitter->pmt_pids[pid] ) { - if ( 2 == splitter->pmt_pids[pid] ) { + if ( 2 == sp->pmt_pids[pid] ) { /* PMT の追跡 */ - int random_access; - DemuxTs((sptr+s_offset), splitter, pid, &random_access); + DemuxTs((sptr+s_offset), sp, pid); } /* pids[pid] が 1 は残すパケットなので書き込む */ - if ( 1 == splitter->pids[pid] ) { - len_pes = 0; - random_access = 0; - /* PCR 解析テスト */ - if (parse_pcr(&pcr_h, &pcr_l, (sptr+s_offset)) == 0){ - pcr = pcr_h * 300 + pcr_l; - /* PCR の種にするものは、Program の PCR として指定されたPacketIDのものとすること */ - /* STC は PCR を種にする */ - splitter->stc = pcr; -// printf("PID[%d] pcr_h=[%llx] pcr_l=[%d] PCR[%f]\n",pid, pcr_h, pcr_l, ((double)pcr/(90000*300))); - } - /* TS処理 */ - if (DemuxTs((sptr+s_offset), splitter, pid, &random_access) == 0 ) { -#if 0 - /* とりあえずやっつけ */ - /* パケットの画面表示時刻(pts) >= 録画開始時刻(cue_time) となった場合に録画開始 */ -// if ( ((pts*300) >= splitter->cue_time) && (splitter->cue_time != -1)) { - if ( ((splitter->esbuf[pid].pts) >= splitter->program[sid].cue_time) && (splitter->program[sid].cue_time != -1)) { - splitter->cue_time = 0; - splitter->status = 0; - clock_gettime(CLOCK_REALTIME, &local_timespec); - printf("start recording. time_sec[%d] nsec[%d].\n", local_timespec.tv_sec%60, local_timespec.tv_nsec); - } -#endif - } - //search_mpeg_system_header(sptr+s_offset); - /* - * STCが cue_time を経過したら録画開始 - */ - if ( splitter->cue_time != -1 && splitter->cue_time <= splitter->stc ) { - memcpy(dptr + d_offset, sptr + s_offset, LENGTH_PACKET); - d_offset += LENGTH_PACKET; - dbuf->size += LENGTH_PACKET; - if ( splitter->status ) { - struct timespec local_timespec; - clock_gettime(CLOCK_REALTIME, &local_timespec); - printf("start recording. time_sec[%d] nsec[%d].\n", local_timespec.tv_sec%60, local_timespec.tv_nsec); - splitter->status = 0; + if ( (1 == sp->pids[pid]) ) { + /* PCRとSTCの処理 */ + int pcr_index; + pcr_index = search_pcr_pid(sp, pid); + if ( sp->pcr_pids[pid] == 1 && pcr_index != -1) { /* PCRか否か */ + ret = parse_pcr(&pcr_h, &pcr_l, (sptr+s_offset)); + /* + * PCR は複数 ServiceID(ProgramID)で重複利用される場合がある + * PCR を参照する複数の ServiceID(ProgramID)分ループ + */ + for (i=0; i < sp->pcr[pcr_index].sid_nb; i++) { + sid = sp->pcr[pcr_index].sid[i]; + program = &(sp->program[sid]); + /* こっから */ + if ( ret == 0 ) { /* PCR の解析に成功 */ + program->stc = pcr_h * 300 + pcr_l; /* PCR受信時にSTCを補正*/ + if ( program->pcr1 == 0 ) { + program->pcr1 = program->stc; + printf("pcr1 pid[%d] sid[%d] packet_nb[%d] sid_nb[%d] i[%d]\n", + pid, sid, packet_nb, sp->pcr[pcr_index].sid_nb, i); + } else if ( program->pcr2 == 0 ) { +// printf("pcr2 pid[%d] sid[%d] packet_nb[%d], p_packet_nb[%d] sid_nb[%d] i[%d]\n", +// pid, sid, packet_nb, program->pcr_packet_nb, sp->pcr[pcr_index].sid_nb, i); + program->pcr2 = program->stc; + program->pcr_incr = (program->pcr2 -program->pcr1) + /(packet_nb -program->pcr_packet_nb); + printf("pcr2 pid[%d] sid[%d] pcr_incr[%llu]\n", + pid, sid, program->pcr_incr); + } else { + /* PCR処理済み */ + ; /* 得に処理無し */ + } + if ( (program->cue == INT64_MAX ) && + (sp->arg_cue != NULL) && + (sp->time_tot != 0) ) { /* 録画開始時刻指定時 */ + /* + * 録画開始時刻 = STC +(CUE -TOT)*27MHz + * +(TOT取得時から現在までのパケット数の増分)*パケットあたりの進む時間 + * -0.49秒(この数字は適当) + * TOT/STCで時間調整して、GOP先頭から出すので攻めてしまっていい気がする + */ + program->cue = program->stc + +(sp->time_cue -sp->time_tot)*27e6 + +(packet_nb -sp->tot_packet_nb)*program->pcr_incr + -(27e6*49/100); + printf("STC[%llu] CUE[%llu] SID[%d]\n", + program->stc, program->cue, sid); + } + program->pcr_packet_nb = packet_nb; + program->packet_nb = packet_nb; + } else if ( program->pcr_incr ) { /* PCRの解析に失敗且つpcr_incr変数の計算済み*/ + /* STCを成長させる */ + program->stc += (packet_nb -program->packet_nb)*program->pcr_incr; + program->packet_nb = packet_nb; + } else { + ; /* STCを進める要素が揃ってません */ + } + } /* for */ + } else { /* 処理対象パケットはPCRではない */ + program = &(sp->program[sid]); + if ( program->pcr_incr ) { /* PCRを受信しない且つpcr_incr変数の計算済み */ + /* STCを成長させる */ + program->stc += (packet_nb -program->packet_nb)*program->pcr_incr; + program->packet_nb = packet_nb; + } else { /* それ以外 */ + ; /* STCを進める要素が揃ってません */ } } +#if 0 +// NHK Gを ALL とすると SID 1024 しか出ない...orz.. + if ( !(packet_nb % 1000) ) { + program = &(sp->program[sid]); + printf("STC[%llu] SID[%d]\n", program->stc, sid); + } +#endif + /* TS処理 */ + DemuxTs((sptr+s_offset), sp, pid); + memcpy(dptr + d_offset, sptr + s_offset, LENGTH_PACKET); + d_offset += LENGTH_PACKET; + dbuf->size += LENGTH_PACKET; } break; } /* switch */ - s_offset += LENGTH_PACKET; + packet_nb += 1; /* パケット受信数加算 */ } - return(TSS_SUCCESS); } @@ -574,6 +611,7 @@ if(service_id == atoi(*p)) { /* 録画対象の pmt_pids は 1 とする */ /* 録画対象の pmt の pids は 1 とする */ + /* 対応する pid_sid_table に サービスID(ProgramID) を入れる */ pid = GetPid(&buf[i + 2]); *(pmt_pids+pid) = 1; *(pids+pid) = 1; @@ -782,6 +820,7 @@ return(TSS_SUCCESS); } + /** * PMT 解析処理 * @@ -800,22 +839,113 @@ int pcr; int epid; int av_flag = 0; + int i = 0; + int j = 0; + int pcr_found = 0; +/* デバッグ用 PMT情報表示 */ +#define PmtDebug (1) + +#ifdef PmtDebug + printf("AnalyzePmt start. Tree List enable.\n"); + printf("SID[%d][0x%04x]\n", sid, sid); +#endif // Nall = ((buf[2] & 0x0F) << 4) + buf[3]; Nall = ((buf[2] & 0x0F) << 8) + buf[3]; +// ここで受け取るのはTSパケットではなく、セクションの先頭ポインタであるのでsizeで見る +// if(Nall > LENGTH_PACKET) +// Nall = LENGTH_PACKET - 8; /* xxx workaround --yaz */ + if(Nall > size) + Nall = size -8; /* get version */ sp->program[sid].pmt_version = get_pmt_version(buf); +#ifdef PmtDebug + printf(" pmt_version[%02x]\n", sp->program[sid].pmt_version); +#endif // PCR pcr = GetPid(&buf[9]); sp->pids[pcr] = 1; sp->program[sid].pcr_packet_id = pcr; + sp->pid_sid_table[pcr] = sid; /* PCRは重複する可能性があるので方式がよろしくない */ + sp->pcr_pids[pcr] = 1; + + /* PCRの重複チェック(複数ServiceID(ProgramID)) */ + for( i=0; i < sp->pcr_nb; i++ ) { + if ( sp->pcr[i].pid == pcr ) { + /* 発見 */ + for ( j=0; j < sp->pcr[i].sid_nb; j++ ) { + /* 同一SIDが既に登録されているか確認 */ + if ( sp->pcr[i].sid[j] == sid ) { + pcr_found = 1; + break; + } + } + if ( pcr_found ) { + /* 同一SIDが既に登録されている */ +#ifdef PmtDebug + printf(" same sid found pcr[%d] sid[%d]\n", pcr, sid); +#endif + break; + } + /* 重複PCR発見 */ +#ifdef PmtDebug + printf(" same pcr found pcr[%d] sid[%d] sid_nb[%d], i[%d]\n", pcr, sid, sp->pcr[i].sid_nb, i); +#endif + sp->pcr[i].sid[sp->pcr[i].sid_nb] = sid; + sp->pcr[i].sid_nb += 1; + pcr_found = 1; + break; + } + } + + if ( ! pcr_found ) { + /* PCR管理領域更新 */ +#ifdef PmtDebug + printf(" new pcr found pcr[%d] sid[%d], pcr_nb[%d]\n", pcr, sid, sp->pcr_nb); +#endif + sp->pcr[sp->pcr_nb].pid = pcr; + sp->pcr[sp->pcr_nb].sid[0] = sid; + sp->pcr[sp->pcr_nb].sid_nb = 1; + sp->pcr_nb += 1; + } +#ifdef PmtDebug + printf(" PCR PacketID[%d][0x%04x]\n", pcr, pcr); +#endif // N = ((buf[11] & 0x0F) << 4) + buf[12] + 16 + 1; N = ((buf[11] & 0x0F) << 8) + buf[12] + 12 + 1; // printf("NAll[%d] N[%d]\n", Nall, N); + // ECM + //int p = 17; + int p = 13; + while(p < N) { + if ( p > size -4) { + break; + } + uint32_t cat_pid; + uint32_t tag; + uint32_t len; + + tag = buf[p]; + len = buf[p+1]; + p += 2; + + if(tag == 0x09 && len >= 4 && p+len <= N) { +// ca_pid = ((buf[p+2] << 8) | buf[p+3]) & 0x1fff; + cat_pid = (AV_RB16(buf+p+2)) & 0x1fff; + sp->pids[cat_pid] = 1; + sp->cat_pids[cat_pid] = 1; + sp->pid_sid_table[cat_pid] = sid; /* CATも複数ServiceIDで重複がある */ +#ifdef PmtDebug + printf(" CAT PacketID[%d][0x%04x]\n", cat_pid, cat_pid); +#endif + } + p += len; + } + /* * ISO/IEC 13818-1:2000(E) Table 2-29 - Stream type assignments * Value Desctiption @@ -846,30 +976,35 @@ */ // ES PID - while (N < Nall + 8 - 4) - { + while (N < Nall + 8 - 4) { av_flag = 0; - if ( N > size ) { - break; - } // ストリーム種別が 0x0D(type D)は出力対象外 - if (0x0D != buf[N]) - { + if (0x0D != buf[N]) { epid = GetPid(&buf[N + 1]); sp->pids[epid] = 1; + sp->pid_sid_table[epid] = sid; if ( buf[N] == 0x02 ) { /* 13818-2 Video */ sp->program[sid].video[sp->program[sid].video_nb] = epid; sp->program[sid].video_nb += 1; - av_flag = 1; + av_flag = TSS_STREAM_TYPE_VIDEO; +#ifdef PmtDebug + printf(" VIDEO PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); +#endif } else if ( (buf[N] == 0x04) || (buf[N] == 0x0f) ) { /* 13818-3 Audio or 13818-7 Audio with ADTS transport syntax */ sp->program[sid].audio[sp->program[sid].audio_nb] = epid; sp->program[sid].audio_nb += 1; - av_flag = 2; + av_flag = TSS_STREAM_TYPE_AUDIO; +#ifdef PmtDebug + printf(" AUDIO PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); +#endif } else { +#ifdef PmtDebug + printf(" OTHER PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); +#endif ; /* A/V どちらでもないものはとりあえずスルー */ } - if ( av_flag ) { + if ( av_flag && sp->esout ) { /* ESバッファはNULLか? */ if ( sp->esbuf[epid] == NULL ) { sp->esbuf[epid] = malloc(sizeof(splitesbuf_t)); @@ -879,8 +1014,10 @@ } sp->esbuf[epid]->size = 0; sp->esbuf[epid]->Program = &(sp->program[sid]); -// creat_filename(sp->filename, sp->esbuf[epid]->filename, sid, epid, av_flag, &(sp->esbuf[epid])); - creat_filename(sp, sid, epid, av_flag); + sp->esbuf[epid]->fd = -1; + if ( creat_es_file(sp, sid, epid, av_flag) ) { + return TSS_ERROR; + } } /* PESバッファはNULLか? */ if ( sp->pesbuf[epid] == NULL ) { @@ -897,6 +1034,9 @@ // N += 4 + (((buf[N + 3]) & 0x0F) << 4) + buf[N + 4] + 1; N += 4 + (((buf[N + 3]) & 0x0F) << 8) + buf[N + 4] + 1; } +#ifdef PmtDebug + printf("AnalyzePmt finish.\n"); +#endif return TSS_SUCCESS; } @@ -994,7 +1134,6 @@ /* pesbufにデータを追加 (ret. 0:success / -1:error) */ static int pesbuf_add(splitpesbuf_t *pesbuf, const uint8_t *data, int len){ if(pesbuf->size + len > sizeof pesbuf->buffer){ - //assert(0); return -1; } memcpy(pesbuf->buffer +pesbuf->size, data, len); @@ -1033,7 +1172,7 @@ /** * TSの解析とDemuxを行う */ -static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid, int *random_access) +static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid) { /* * PES先頭までの長さは @@ -1049,8 +1188,8 @@ int pes_started; int adaptation_field_control; int payload_unit_start_indicator; - int random_access_indicator = 0; - int sid = sp->pid_sid_table[pid]; +// int random_access_indicator = 0; + int sid = sp->pid_sid_table[pid]; /* SIDをPIDから引いているが、PCRとCATは重複しているので注意*/ payload_offset = LENGTH_TS_HEADER; @@ -1068,7 +1207,7 @@ } else if ( adaptation_field_control == 0x03 ) { /* アダプテーションフィールド+ペイロードの場合 */ if ( packet[LENGTH_TS_HEADER] != 0 ) { - random_access_indicator = (packet[5] & 0x40) >> 6; +// random_access_indicator = (packet[5] & 0x40) >> 6; } /* ペイロード開始位置 = TSヘッダ長 + アダプテーションフィールド長 + 1 */ payload_offset += packet[LENGTH_TS_HEADER] + 1; @@ -1100,6 +1239,17 @@ } return 0; /* PMT の場合は処理終わり */ } + if ( !sp->esout ) { + /* ES出力しない場合はPES蓄積不要 */ + return 0; + } + if ( sp->cat_pids[pid] == 1 ) { + return 0; /* CATは蓄積しない */ + } + if ( sp->pesbuf[pid] == NULL ) { + /* PES蓄積不要である場合も蓄積しない */ + return 0; + } /* payload_unit_start_indicatorを処理(2) */ /* 必要に応じ、蓄積済みPESの処理と、PES蓄積開始を行う */ @@ -1107,7 +1257,8 @@ /* PES開始 */ if ( pes_started ) { /* バッファにデータがあればPES終端なので処理してクリア */ - pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid, random_access_indicator); +// pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid, random_access_indicator); + pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid); pesbuf_clear(sp->pesbuf[pid]); } else { @@ -1124,6 +1275,8 @@ return 0; } +#if 0 +未使用なため削除 /* PMT_PID から Program(Service ID)を確定させる */ static int search_pmt_program(splitter *sp, int pid) { @@ -1136,6 +1289,7 @@ } return -1; } +#endif /* esbufが空か判定 (ret. 0:not empty / 1: empty) */ static int esbuf_empty(splitesbuf_t *esbuf){ @@ -1143,9 +1297,10 @@ } /* esbufをクリア */ -void esbuf_clear(splitesbuf_t *esbuf){ - esbuf->random_access_indicator = 0; +void esbuf_clear(splitesbuf_t *esbuf, uint64_t pts, uint64_t dts){ esbuf->size = 0; + esbuf->pts = pts; + esbuf->dts = dts; } /* esbufにデータを追加 (ret. 0:success / -1:error) */ @@ -1161,7 +1316,8 @@ /* * PESを解析してESを出力する */ -static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator) +//static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator) +static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid) { int len_pesh = 0; int code = 0; @@ -1184,7 +1340,8 @@ int64_t audio_pts = 0; int adts_freq = 0; int64_t adts_frame_time = 0; - int audio_accumulation = 0; + int gop_start = -1; + /* ありがとう */ /* ありがとうとコメントを書くと、 * 動作がよくなる @@ -1397,61 +1554,83 @@ len_pesh_supposed += pes_extension_flags2; } } + if ( pid != 6417 && pid != 6418 ) { +// printf("es start? pid[%d]\n", pid); + } + /* ES蓄積管理処理 */ payload_length = pesbuf->size -payload_offset; - if ( data_alignment_indicator ) { +// if ( data_alignment_indicator ) { /* data_alignment_indicator 区切りでESを出力する */ if ( es_started ) { /* ES にデータが蓄積されている */ - if ( is_video_stream(pid, esbuf) && !(esbuf->started) ) { /* VIDEO である場合 */ - /* random_access ビットが立っている場合は、GOP先頭である */ - if ( random_access_indicator ) { /* TS の random_access ビットが立ったものがきているか? */ - /* 該当ストリームを蓄積開始する */ + /* + * ビデオをファイル出力し始める条件(1. 2. を満たすこと) + * 1. ESにGOP先頭を含む + * 2. PTSがCUEの時刻を過ぎていること( CUE <= PTS ) + */ + if ( (is_video_stream(pid, esbuf) == 0) && !(esbuf->started) ) { + /* VIDEO0 を同期の基準とする */ + gop_start = search_gop_start_code(esbuf); + if ( (gop_start != -1) && /* ESバッファにGOP_START_CODEが存在するか? */ + (esbuf->Program->cue <= esbuf->pts*300) ) { /* CUEを過ぎている? */ + /* 該当ストリームをファイル出力開始する */ 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; + /* GOP先頭を含まないものはクリア */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + } + } else if ( (is_video_stream(pid, esbuf) != -1) && !(esbuf->started) ) { + /* VIDEO0 以外のものはVIDEO0 が開始するまでクリア */ + if ( !(esbuf->Program->video_start) ) { + /* + * VIDEO0 が始まってない場合、 + * VIDEON は常にクリア + */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + } else { + /* + * VIDEO0 が始まっている場合、 + * VIDEON の録画を開始 + */ + esbuf->started = 1; } } - /* AAC の実験コードここから */ /* - * オーディオを出力し始める条件(1. 2. を満たすこと) + * オーディオをファイル出力し始める条件(1. 2. を満たすこと) * 1. 動画の蓄積は開始されている * 2. 動画のGOPの1番目のIピクチャのPTSとオーディオのPTSを比較して以下のどちらかを満たすこと - * 2.1. 差分が11msec以内 + * 2.1. 差分が11msec以内(1000*90k/AACのサンプリング周波数) + * 1000 : ADTSデータの1フレームのサンプル数 + * 90k : PTSの周波数 * 2.2. 過ぎている(過ぎている場合はオーディオESの蓄積の継続と次のGOP狙いにする?) - * #GOP先頭にこだわり過ぎると録画を逃すという線もあるので、適当に手を打つのも手 + * #動画よりオーディオ側の方が先にESバッファの蓄積を始めるハズなので気にすること無いかなぁ… */ - else if ( (is_audio_stream(pid, esbuf) != -1 ) && !(esbuf->started) ) { + else if ( (is_audio_stream(pid, esbuf) != -1) && !(esbuf->started) ) { if ( !(esbuf->Program->video_start) ) { /* * VIDEO が始まってない場合、 - * ESバッファの余裕がある限り蓄積続けちゃえばいいんでない? + * ESバッファの余裕がある限り、オーディオをESバッファに蓄積し続ける */ - audio_accumulation = 1; /* ESバッファにオーディオを追記する */ - if ( esbuf->size > sizeof esbuf->buffer -payload_length ){ + if ( esbuf->size + payload_length > sizeof esbuf->buffer ){ /* 溢れそうになったらクリア */ - esbuf_clear(esbuf); - esbuf->pts = pesbuf->pts; - esbuf->dts = pesbuf->dts; - audio_accumulation = 0; + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); } - } - 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); + } else if ( esbuf->Program->video_start ) { /* video 蓄積が開始されている?*/ + printf("audio stream. pid[%d] a_pts[%llu] v_pts[%llu] size[%d].\n", pid, esbuf->pts, esbuf->Program->video_pts, esbuf->size); audio_lipsync_offset = 0; audio_pts = esbuf->pts; adts_freq = AnalyzeAdifHeader(esbuf); adts_frame_time = (int64_t)((float)1000*90000/adts_freq); /* PTSは90KHz */ + /* オーディオをフレーム単位で捨ててPTSを進める */ 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のデータを取得 */ + i = next_adts_start_code(esbuf, audio_lipsync_offset); /* 次のAACのデータを取得 */ if ( i != -1 ) { /* AACデータの終端か? */ audio_lipsync_offset += i; } else { + /* バッファ終端まで進めたが、オーディオPTSの方が古い場合ESバッファはクリアする */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); break; } printf("audio stream drop. pid[%d] pts[%llu].\n", pid, audio_pts); @@ -1463,33 +1642,30 @@ esbuf->buffer, esbuf->size -audio_lipsync_offset); esbuf->size -= audio_lipsync_offset; - esbuf->started = 1; + esbuf->started = 1; /* オーディオのファイル出力を有効化 */ } - } - else { + } else { ; /* 該当するものは無いはず */ } + } else { + /* 得に処理なし */ + ; } - /* AAC の実験コードここまで */ /* バッファをファイルに出力してクリア */ - if ( esbuf->started ) { + if ( esbuf->started ) { /* 該当ストリームはファイル出力の有効化をされている? */ esbuf_write(esbuf); - esbuf_clear(esbuf); - esbuf->pts = pesbuf->pts; - esbuf->dts = pesbuf->dts; + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); } } else { /* ES蓄積を新たに開始 */ es_started = 1; + esbuf->pts = pesbuf->pts; + esbuf->dts = pesbuf->dts; } - } + //} /* 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); } @@ -1497,61 +1673,52 @@ return 0; } -/* PIDはesbufのAUDIO STREAMの一つであるか? */ +/* Program の N 番目の AUDIO STREAM であるかを返却する */ static int is_audio_stream(const int pid, splitesbuf_t *esbuf) { int i = 0; - int found = 0; program_t* program = esbuf->Program; while (i < program->audio_nb) { if (program->audio[i] == pid) { - found = 1; - break; + return i; } i++; } - return found; + return -1; } -/* PIDはesbufのVIDEO STREAMの一つであるか? */ +/* Program の N 番目の VIDEO STREAM であるかを返却する */ static int is_video_stream(const int pid, splitesbuf_t *esbuf) { int i = 0; - int found = 0; program_t* program = esbuf->Program; while (i < program->video_nb) { if (program->video[i] == pid) { - found = 1; - break; + return i; } i++; } - return found; + return -1; } /* * ESをファイル出力する + * エラーハンドリングしてないね… */ -//int esbuf_write(splitesbuf_t *esbuf, int pid) -int esbuf_write(splitesbuf_t *esbuf) +static int esbuf_write(splitesbuf_t *esbuf) { int remain = esbuf->size; -//#define ES_FILE "/tmp/es.%d" -// char filename[1024]; -// filename[0] = '\0'; -// umask(0133); -// snprintf(filename, sizeof(filename), ES_FILE, pid); -// fd = open(filename, O_CREAT|O_APPEND|O_RDWR, 00644); while(remain > 0) { remain -= write(esbuf->fd, esbuf->buffer+(esbuf->size-remain), remain); } -// close(fd); return 0; } +#if 0 +未使用なため駆除 /* * packet dump */ @@ -1581,6 +1748,7 @@ } putchar('\n'); } +#endif /* * TOT の JST_time を解析する @@ -1646,25 +1814,8 @@ return ((p[6] >> 1) & 0x1f); } - #if 0 -void forward_stc(timespec *stc, timespec offset) -{ - struct timespec local_timespec; - - clock_gettime(CLOCK_REALTIME, &local_timespec); - - stc->tv_sec = local_timespec.tv_sec + offset.tv_sec; - stc->tv_nsec = local_timespec.tv_nsec + offset.tv_nsec; - if ( stc->tv_nsec >= 1 * 1000 * 1000 * 1000 ) { - stc->tv_nsec -= 1 * 1000 * 1000 * 1000; - stc->tv_sec += 1; - } else if ( stc->tv_nsec < 0 ) { - stc->tv_nsec = 1 * 1000 * 1000 * 1000 + stc->tv_nsec; - stc->tv_sec -= 1; - } -} -#endif +未使用なため駆除 void search_mpeg_system_header(const uint8_t *packet) { int i; @@ -1676,28 +1827,30 @@ } } } +#endif /* * この関数では、現在の仕様では、先頭位置の「次の」ADTS start codeまでの長さを返却する - * ret == 0 : 仕様上あり得ない + * ret == 0 : 仕様上あり得ない(esbuf先頭はヘッダ先頭であるため) * ret > 0 : 見つかった場合 * ret == -1 : 見つからなかった場合 */ -static int esbuf_adts_start_code_prefix(splitesbuf_t *esbuf, int offset) +static int next_adts_start_code(splitesbuf_t *esbuf, int offset) { /* - * start code prefix のうち、先頭12bit は 1 固定であるが、13bit 目は id に該当 - * MPEG4なオーディオが来た場合に対応出来ないので、13bit 目は見ないように改造するべき + * start code prefix のうち、先頭12bit は 1 固定 */ - uint8_t adts_start_code_prefix[2] = {0xFF, 0xF8}; /* とりあえず決め打ち */ + uint16_t adts_start_code = 0xfff0; int i = offset +1; + uint16_t startcode = 0; /* 小さすぎる */ - if(esbuf->size -offset < sizeof adts_start_code_prefix){ + if(esbuf->size -offset < sizeof(adts_start_code)){ return -1; } - for(; i < esbuf->size - sizeof adts_start_code_prefix; i++) { - if(!memcmp(esbuf->buffer + i ,adts_start_code_prefix, sizeof adts_start_code_prefix)){ + for(; i < esbuf->size - sizeof(adts_start_code); i++) { + startcode = AV_RB16(esbuf->buffer+i); + if( startcode == adts_start_code ) { /* 該当位置から12bit連続1が立っているか? */ #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) ); @@ -1711,22 +1864,22 @@ /* ADIF HEADER解析 */ static int AnalyzeAdifHeader(splitesbuf_t *esbuf) { - int id = 0; /* 0:MPEG-4 1:MPEG-2 */ - int layer = 0; /* 常に 0x00 */ - int protection_absent = 0; /*保護属性 0:保護なし 1:保護あり */ - int profile = 0; /* 00:MAIN 01:LC 10:SSR 11:(reserved) */ - int sampling_frequency_index = 0; /* サンプリング周波数テーブル値 */ - int private_bit = 0; /* private bit */ - int channel_configuration = 0; /* チャンネル数 */ + int id = 0; /* 0:MPEG-4 1:MPEG-2 */ + int layer = 0; /* 常に 0x00 */ + int protection_absent = 0; /* 保護属性 0:保護なし 1:保護あり */ + int profile = 0; /* 00:MAIN 01:LC 10:SSR 11:(reserved) */ + int sampling_frequency_index = 0; /* サンプリング周波数テーブル値 */ + int private_bit = 0; /* private bit */ + int channel_configuration = 0; /* チャンネル数 */ int original_copy = 0; - int home = 0; /* homeってなに? */ - int copyright_identification_bit = 0; /* 著作権証明ビット */ - int copyright_identification_start = 0; /* 著作権証明開始ビット */ - int aac_frame_length = 0; /* AACフレーム長 */ - int adts_buffer_fullness = 0; /* ADTSバッファ残量 */ - int no_raw_data_blocks_in_frame = 0; /* データブロックまでの残量 */ + int home = 0; /* homeってなに? */ + int copyright_identification_bit = 0; /* 著作権証明ビット */ + int copyright_identification_start = 0; /* 著作権証明開始ビット */ + int aac_frame_length = 0; /* AACフレーム長 */ + int adts_buffer_fullness = 0; /* ADTSバッファ残量 */ + int no_raw_data_blocks_in_frame = 0; /* データブロックまでの残量 */ /* - * サンプリング周波数テーブル(ヘッダのINDEXが添字) + * サンプリング周波数テーブル(ヘッダのsampling_frequency_indexが添字) * 単位:Hz */ int sampling_frequency_table[16] = @@ -1847,10 +2000,11 @@ return (*p & 0x03); } +#define GOP_START_CODE (0x000001b8) /* GOP START CODE を検索する */ static int search_gop_start_code(splitesbuf_t *esbuf) { - uint8_t gop_start_code[4] = {0x00, 0x00, 0x01, 0xb8}; + uint32_t gop_start_code = GOP_START_CODE; int i; /* 小さすぎる */ @@ -1858,25 +2012,26 @@ return -1; } for(i = 0; i < esbuf->size - sizeof gop_start_code; i++) { - if(!memcmp(esbuf->buffer +i ,gop_start_code, sizeof gop_start_code)){ + if ( (AV_RB32(esbuf->buffer +i)) == gop_start_code ) { return i; } } return -1; } -/* ES 出力するファイル名を決定する */ -//static int creat_filename(char *base, char *filename, int sid, int pid, int av_flag ,splitesbuf_t *esbuf) -static int creat_filename(splitter *sp, int sid, int pid, int av_flag) +/* ES 出力するファイルを作成する */ +static int creat_es_file(splitter *sp, int sid, int pid, int av_flag) { /* * 出力ESファイルの命名規則は以下とする * - * ファイル名のベースは出力TSファイル名 - * 出力先パスも出力TSファイル名から取得 - * ファイル名のうち、「.ts」を削除して、sidとepidをつけて、「.es」とする - * .m2v とか .m2a とかそういう名前って好きではないのだけど...ISOに既定ないし - * オーディオなのか、ビデオなのかって情報はあるけど + * ファイル名のベースは --es オプションの引数 + * 以下の形式で命名して、ファイルオープンまで実施する。 + * ファイル名prefix_SID_AVのプログラム内番号.m2v + * ファイル名prefix_SID_AVのプログラム内番号.aac + * + * !!注意!! + * MPEG-2/MPEG-4 AAC 以外のオーディオが来た場合の処理が未実装 */ char filename[PATH_MAX]; @@ -1887,10 +2042,11 @@ char suffix_v[] = "m2v"; filename[0] = '\0'; - if ( av_flag == 1 ) { + /* ちょっとこの辺のコードイケてないので後から直すかも */ + if ( av_flag == TSS_STREAM_TYPE_VIDEO ) { suffix = suffix_v; av_nb = sp->program[sid].video_nb -1; - } else if ( av_flag == 2 ){ + } else if ( av_flag == TSS_STREAM_TYPE_AUDIO ){ suffix = suffix_a; av_nb = sp->program[sid].audio_nb -1; } else { @@ -1900,7 +2056,7 @@ size = strlen(sp->filename); if ( size +16 < sizeof(filename) ) { - snprintf(filename, sizeof(filename), "%s_%04d_%02d.%s", sp->filename, sid, av_nb, suffix); + snprintf(filename, sizeof(filename), "%s_%05d_%02d.%s", sp->filename, sid, av_nb, suffix); filename[PATH_MAX-1] = '\0'; } else { /* ファイル名つけられなくて困るでござるの巻 */ @@ -1908,8 +2064,70 @@ } umask(0133); if ( !(sp->esbuf[pid]->fd = open(filename, O_CREAT|O_APPEND|O_RDWR, 00644)) ) { - fprintf("cannot open es out file. file[%s].\n", filename); + fprintf(stderr, "cannot open es out file. file[%s].\n", filename); return -1; } return 0; } + +static time_t cue2time(char *yyyymmddhhmiss) +{ + struct tm cue_tm; + time_t cue_time; + char *p; + int i, j; + char str_yyyy[5]; + char str_mm[3]; + char str_dd[3]; + char str_hh[3]; + char str_mi[3]; + char str_ss[3]; + str_yyyy[0] = '\0'; + str_mm[0] = '\0'; + str_dd[0] = '\0'; + str_hh[0] = '\0'; + str_mi[0] = '\0'; + str_ss[0] = '\0'; + + p = yyyymmddhhmiss; + i = strlen(p); + j = strspn(p, LIST_DECIMAL); + if ( i != j && i != 14 ) { + /* 数字以外混ぜるな */ + return -1; + } + strncpy(str_yyyy, yyyymmddhhmiss, 4); + strncpy(str_mm, yyyymmddhhmiss+4, 2); + strncpy(str_dd, yyyymmddhhmiss+6, 2); + strncpy(str_hh, yyyymmddhhmiss+8, 2); + strncpy(str_mi, yyyymmddhhmiss+10, 2); + strncpy(str_ss, yyyymmddhhmiss+12, 2); + str_yyyy[4] = '\0'; + str_mm[2] = '\0'; + str_dd[2] = '\0'; + str_hh[2] = '\0'; + str_mi[2] = '\0'; + str_ss[2] = '\0'; + + cue_tm.tm_sec = atoi(str_ss); + cue_tm.tm_min = atoi(str_mi); + cue_tm.tm_hour = atoi(str_hh); + cue_tm.tm_mday = atoi(str_dd); + cue_tm.tm_mon = atoi(str_mm)-1; + cue_tm.tm_year = atoi(str_yyyy)-1900; + cue_tm.tm_isdst = -1; + cue_time = mktime(&cue_tm); + return cue_time; +} + +/* PCR の PID を検索する */ +static int search_pcr_pid(splitter *sp, int pid) +{ + int i; + for ( i=0; i < MAX_SERVICES; i++ ) { + if ( sp->pcr[i].pid == pid ) { + return i; + } + } + return -1; +}
--- a/recpt1/tssplitter_lite.h Thu Apr 29 02:02:42 2010 +0900 +++ b/recpt1/tssplitter_lite.h Wed May 05 20:43:43 2010 +0900 @@ -119,13 +119,14 @@ typedef struct _program_t { int64_t stc; - int64_t cue_time; /* 録画開始時刻 */ + int64_t cue; /* 録画開始時刻 */ int pmt_packet_id; /* 該当Program(Service ID)に対応するPMT */ int pmt_version; /* 該当Program(Service ID)に対応するPMTのVersion */ int pcr_packet_id; /* 該当Program(Service ID)のPCRを保持するPID */ int64_t pcr1; /* PCR1 */ int64_t pcr2; /* PCR2 */ - int packet_nb; /* PCR計算用カウンタ */ + int pcr_packet_nb; /* 直前のPCRを受信したときのパケット数 */ + int packet_nb; /* 直前のProgramの処理を実施したときのパケット数 */ int64_t pcr_incr; /* 該当Program(Service ID)に於いて、1つのTSパケットを処理した時に経過する(と想定する時間) */ int video_start; /* VODEO0を蓄積開始している? */ int64_t video_pts; /* 最後に処理したVODEO0のESのPTS */ @@ -133,8 +134,6 @@ int audio_nb; /* PMT に存在する音声ストリームの数 */ int video[MAX_VIDEO]; /* PS出力する場合に使うかも */ int audio[MAX_AUDIO]; /* PS出力する場合に使うかも */ - //splitpesbuf_t *pesbuf; - //splitesbuf_t *esbuf; } program_t; /* * program_t をサービスID分準備して、使用するイメージでいたけど、 @@ -146,11 +145,10 @@ typedef struct _splitpesbuf_t { 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]; + u_char buffer[128*1024]; } splitpesbuf_t; typedef struct _splitesbuf_t @@ -165,12 +163,28 @@ u_char buffer[3*1024*1024]; } splitesbuf_t; +/* PCR 共有用構造体 */ +typedef struct _pcr_t +{ + int pid; + int sid_nb; /* PCRを参照しているServiceID(ProgramID)の数 */ + int sid[MAX_SERVICES]; +} pcr_t; + + /** * splitter構造体 */ typedef struct splitter { + char *filename; /* ファイル名を上位からもらってくるためのポインタ */ + char *arg_cue; /* 引数で取得してきた録画開始時刻(HHMISS) */ + int esout; /* ES出力する? */ unsigned char pids[MAX_PID]; unsigned char pmt_pids[MAX_PID]; + uint8_t cat_pids[MAX_PID]; + uint8_t pcr_pids[MAX_PID]; /* PCRは複数ServiceID(ProgramID)で共有される */ + pcr_t pcr[MAX_SERVICES]; + int pcr_nb; unsigned char* pat; char** sid_list; unsigned char pat_count; @@ -178,17 +192,13 @@ int pmt_counter; int avail_pmts[MAX_SERVICES]; int num_pmts; -// struct timespec tot_offset; -// struct timespec STC_timespec; - int64_t stc; - int64_t cue_time; - int status; splitpesbuf_t *pesbuf[MAX_PID]; splitesbuf_t *esbuf[MAX_PID]; program_t *program; int pid_sid_table[MAX_PID]; /* pid to sid の変換を行うためのテーブル */ - char *filename; /* ファイル名を上位からもらってくるためのポインタ */ - int esout; /* ES出力する? */ + time_t time_cue; + time_t time_tot; + int tot_packet_nb; /* TOT受信時のパケット受信数 */ } splitter; /* b25 decoder would hoard up large chank */ @@ -198,7 +208,7 @@ u_char buffer[1024*1024]; } splitbuf_t; -splitter* split_startup(char *sid, char *filename); +splitter* split_startup(char *sid, char *filename, char *cue_time); int split_select(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); void split_shutdown(splitter *sp); int split_ts(splitter *splitter, ARIB_STD_B25_BUFFER *sbuf, splitbuf_t *dbuf);