Mercurial > pt1.oyama
view src/tssplitter_lite.c @ 146:066f33b2213a
EXPERIMENTAL: Select a particular program from multi-channel.
author | Naoya OYAMA <naoya.oyama@gmail.com> |
---|---|
date | Tue, 21 Aug 2012 04:21:11 +0900 |
parents | e72dd5e8d53f |
children | a9f60d56d673 |
line wrap: on
line source
/* -*- tab-width: 4; indent-tabs-mode: t -*- */ /* vim: set ts=4 sts=4 sw=4 noexpandtab number : */ /* tssplitter_lite.c -- split TS stream. Copyright 2009 querulous Copyright 2010-2012 Naoya OYAMA <naoya.oyama@gmail.com> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <fcntl.h> #include <sys/stat.h> #include <math.h> #include <time.h> #include "decoder.h" #include "recpt1.h" #include "tssplitter_lite.h" #include "ushare.h" #include "metadata.h" #ifndef AV_RB32 #define AV_RB32(x) ((((const uint8_t*)(x))[0] << 24) | \ (((const uint8_t*)(x))[1] << 16) | \ (((const uint8_t*)(x))[2] << 8) | \ ((const uint8_t*)(x))[3]) #endif #ifndef AV_RB24 #define AV_RB24(x) ((((const uint8_t*)(x))[0] << 16) | \ (((const uint8_t*)(x))[1] << 8) | \ ((const uint8_t*)(x))[2]) #endif #ifndef AV_RB16 #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) //global extern struct ushare_t *ut; /* prototypes */ static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); static int AnalyzePat(splitter *sp, unsigned char *buf); static int RecreatePat(splitter *sp, unsigned char *buf, int *pos); static char** AnalyzeSid(char *sid); static int AnalyzePmt(splitter *sp, const uint8_t *buf, int sid, const int size); 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 ); 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); //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); 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, 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 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); static int get_adif_id(uint8_t *p); static int get_adif_layer(uint8_t *p); static int get_adif_protection_absent(uint8_t *p); static int get_adif_profile(uint8_t *p); static int get_adif_sampling_frequency_index(uint8_t *p); static int get_adif_private_bit(uint8_t *p); static int get_adif_channel_configuration(uint8_t *p); static int get_adif_original_copy(uint8_t *p); static int get_adif_home(uint8_t *p); static int get_adif_copyright_idication_bit(uint8_t *p); static int get_adif_copyright_idication_start(uint8_t *p); 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_gop_start_code(splitesbuf_t *esbuf); 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解析 */ static char** AnalyzeSid( char* sid) // [in] サービスID(カンマ区切りテキスト) { int i = 0; char** sid_list = NULL; char* p; int CommaNum = 0; /* sid は次の形式の引数を許容する */ /* 指定無し */ /* SID[0] */ /* SID[0],SID[1],...,SID[N-1],SID[N] */ /*カンマの数を数える*/ p = sid; while(*p != '\0') { if( *p == C_CHAR_COMMA ){ CommaNum++; } p++; } /* sid_listの数はカンマの数+2(NULL止めするから) */ sid_list = malloc(sizeof(char*)*(CommaNum+2)); if ( sid_list == NULL ) { fprintf(stderr, "AnalyzeSid() malloc error.\n"); return NULL; } /* sidが空である場合 */ p = sid; if ( strlen(p) == 0 ) { sid_list[0] = NULL; return sid_list; } /* カンマ無し */ if ( CommaNum == 0 ) { sid_list[0] = sid; sid_list[1] = NULL; return sid_list; } /* カンマ区切りで複数指定時 */ i=0; p = sid; /* 文字列端に到達するか、カンマ数が数えた数に達したら終了 */ while((*p != '\0') || i < CommaNum) { /* 現在の処理位置をsid_list[i]にセット */ /* このタイミングの p は * ・sid先頭 * ・[,]の次の文字 * いずれかであるので p を sid_list[i] に代入してよい */ sid_list[i] = p; i++; /* 最初に現れる[,]をNULL文字で置換する */ p = strchr(p, C_CHAR_COMMA); if ( p == NULL ) { /* カンマが見つからない場合は最後の処理対象なので終了 */ break; } *p = '\0'; /* 処理位置をNULLで置換した文字の次の位置に設定する */ p++; } /* 最後のsid_list[n]はNULLポインタで止める */ sid_list[i] = NULL; i=0; while( sid_list[i] != NULL ) { i++; } #if 0 for(i=0; sid_list[i] != NULL; i++) { printf("sid_list[%d]=[%s].\n",i, sid_list[i]); } #endif return sid_list; } /** * 初期化処理 */ splitter* split_startup( char *sid, // [in] サービスID(引数で指定した文字列) char *filename, // [in] 出力ESファイル名(引数で指定したファイル名) char *arg_cue // [in] 録画開始時刻(引数で指定した文字列 YYYYMMDDHHMISS) ) { splitter* sp; int i; sp = malloc(sizeof(splitter)); if ( sp == NULL ) { fprintf(stderr, "split_startup malloc error.\n"); return NULL; } sp->program = malloc( sizeof(program_t) * MAX_SERVICE_ID ); if ( sp->program == NULL ) { fprintf(stderr, "split_startup malloc error.\n"); return NULL; } 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; sp->sid_list = AnalyzeSid(sid); if ( sp->sid_list == NULL ) { free(sp); return NULL; } sp->pat_count = 0xFF; sp->pmt_retain = -1; sp->pmt_counter = 0; 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 = NULL; sp->esout = 0; 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; } /** * 落とすPIDを確定させる */ int split_select( splitter *sp, // [in/out] splitter構造体 ARIB_STD_B25_BUFFER *sbuf // [in] 入力TS ) { int result; // TS解析 result = ReadTs(sp, sbuf); return result; } /** * 終了処理 */ void split_shutdown(splitter* sp) { int i = 0; if ( sp != NULL ) { if ( sp->program != NULL ) { free(sp->program); sp->program = NULL; } if ( sp->pat != NULL ) { free(sp->pat); sp->pat = NULL; } if ( sp->sid_list != NULL ) { 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; } } /** * TS 解析処理 * * 対象のチャンネル番号のみの PAT の再構築と出力対象 PID の抽出を行う */ static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf) { #if 0 splitter *sp, // [in/out] splitter構造体 ARIB_STD_B25_BUFFER *sbuf, // [in] pt1_drvの入力TS #endif int length = sbuf->size; int pid; int result = TSS_ERROR; int index; index = 0; while(length - index - LENGTH_PACKET > 0) { pid = GetPid(sbuf->data + index + 1); // PAT if(PAT == pid) { result = AnalyzePat(sp, sbuf->data + index); if(TSS_SUCCESS != result) { /* 下位の関数内部でmalloc error発生 */ return result; } } // PMT /* 残すpmt_pidである場合には、pmtに書かれている * 残すべきPCR/AUDIO/VIDEO PIDを取得する */ if(sp->pmt_pids[pid] == 1) { /* この中にはPMT毎に一度しか入らないようにしておく */ sp->pmt_pids[pid]++; sp->pmt_counter += 1; DemuxTs(sbuf->data +index, sp, pid); /* AnalyzePmt より DemuxTs の方がアダプテーションフィールドの処理が良いので変更 */ } /* 録画する全てのPMTについて、中にあるPCR/AUDIO/VIDEOのPIDを得る */ /* pmt_counter と pmt_retain が一致する場合に条件は満たされる */ if(sp->pmt_counter == sp->pmt_retain) { result = TSS_SUCCESS; break; } else { result = TSS_ERROR; } index += LENGTH_PACKET; } return(result); } /** * TS 分離処理 */ int split_ts( splitter *sp, // [in] splitterパラメータ ARIB_STD_B25_BUFFER *sbuf, // [in] 入力TS splitbuf_t *dbuf // [out] 出力TS ) { int pid; unsigned char *sptr, *dptr; int s_offset = 0; int d_offset = 0; 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; if (sbuf->size < 0) { return TSS_ERROR; } sptr = sbuf->data; dptr = dbuf->buffer; while(sbuf->size > s_offset) { pid = GetPid(sptr + s_offset + 1); sid = sp->pid_sid_table[pid]; /* PIDからSIDを取得 */ switch(pid) { // PAT case PAT: // 巡回カウンタカウントアップ if(0xFF == sp->pat_count) { sp->pat_count = sp->pat[3]; } else { sp->pat_count += 1; if(0 == sp->pat_count % 0x10) { sp->pat_count -= 0x10; } } sp->pat[3] = sp->pat_count; memcpy(dptr + d_offset, sp->pat, LENGTH_PACKET); d_offset += LENGTH_PACKET; dbuf->size += LENGTH_PACKET; break; case TOT: /* TOT に TDTの情報全てが含まれており、実放送では TOT しか送信されない */ /* TOT は 500msec の誤差が保証されている * 閏秒の場合は最大1.5秒の誤差となる */ 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(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と同じ方針) * * ■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 ( 2 == sp->pmt_pids[pid] ) { /* PMT の追跡 */ DemuxTs((sptr+s_offset), sp, pid); } /* pids[pid] が 1 は残すパケットなので書き込む */ 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); } /** * PAT 解析処理 * * PAT を解析し、出力対象チャンネルが含まれているかチェックを行い、PAT を再構築する */ static int AnalyzePat(splitter *sp, unsigned char *buf) #if 0 unsigned char* buf, // [in] 読み込んだバッファ unsigned char** pat, // [out] PAT 情報(再構築後) unsigned char* pids, // [out] 出力対象 PID 情報 char** sid_list, // [in] 出力対象サービス ID のリスト unsigned char* pmt_pids, // [out] サービス ID に対応する PMT の PID int* pmt_retain // [out] 残すPMTの数 ) #endif { int pos[MAX_PID]; int service_id; int i, j, k; int size = 0; int pid; int result = TSS_SUCCESS; char **p; int sid_found = FALSE; int avail_sids[MAX_SERVICES]; unsigned char *pat = sp->pat; unsigned char *pids = sp->pids; char **sid_list = sp->sid_list; unsigned char *pmt_pids = sp->pmt_pids; char chosen_sid[512]; chosen_sid[0] = '\0'; if(pat == NULL) { /* 初期化 */ sp->pmt_retain = 0; memset(pos, 0, sizeof(pos)); size = buf[7]; /* prescan SID/PMT */ for(i = 17, j = 0; i < (size + 8) - 4; i = i + 4, j++) { avail_sids[j] = (buf[i] << 8) + buf[i+1]; sp->avail_pmts[j] = GetPid(&buf[i+2]); } sp->num_pmts = j; // 対象チャンネル判定 /* size + 8 = パケット全長 */ /* 最終 4 バイトはCRCなので飛ばす */ for(i = 17; i < (size + 8) - 4; i = i + 4) { service_id = (buf[i] << 8) + buf[i+1]; p = sid_list; while(*p) { 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; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); p++; continue; } else if(!strcasecmp(*p, "hd") || !strcasecmp(*p, "sd1")) { /* hd/sd1 指定時には1番目のサービスを保存する */ if(service_id == avail_sids[0]) { pid = GetPid(&buf[i + 2]); *(pmt_pids+pid) = 1; *(pids+pid) = 1; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); } p++; continue; } else if(!strcasecmp(*p, "sd2")) { /* sd2 指定時には2番目のサービスを保存する */ if(service_id == avail_sids[1]) { pid = GetPid(&buf[i + 2]); *(pmt_pids+pid) = 1; *(pids+pid) = 1; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); } p++; continue; } else if(!strcasecmp(*p, "sd3")) { /* sd3 指定時には3番目のサービスを保存する */ if(service_id == avail_sids[2]) { pid = GetPid(&buf[i + 2]); *(pmt_pids+pid) = 1; *(pids+pid) = 1; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); } p++; continue; } else if(!strcasecmp(*p, "1seg")) { /* 1seg 指定時には PMTPID=0x1FC8 のサービスを保存する */ pid = GetPid(&buf[i + 2]); if(pid == 0x1FC8) { *(pmt_pids+pid) = 1; *(pids+pid) = 1; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); } p++; continue; } else if(!strcasecmp(*p, "all")) { /* all指定時には全保存する */ pid = GetPid(&buf[i + 2]); *(pmt_pids+pid) = 1; *(pids+pid) = 1; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); break; } p++; } /* while */ } /* if sid has been specified but no sid found, fall back to all */ if(*sid_list && !sid_found) { for(i = 17; i < (size + 8) - 4; i = i + 4) { service_id = (buf[i] << 8) + buf[i+1]; pid = GetPid(&buf[i + 2]); *(pmt_pids+pid) = 1; *(pids+pid) = 1; pos[pid] = i; sid_found = TRUE; sp->pmt_retain += 1; sp->program[service_id].pmt_packet_id = pid; sp->pid_sid_table[pid] = service_id; sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); } } /* print SIDs */ fprintf(stderr, "Available sid = "); for(k=0; k < sp->num_pmts; k++) { fprintf(stderr, "%d ", avail_sids[k]); /* ut->channel_name にSID番号を入れる * ushare 側(TV表示上)には channel_name を表示させる * TODO 局名も含めて入れたいなぁ */ #define CHANNEL_NAME_LENGTH (64) ut->channel_name[ut->nr_channel] = malloc(CHANNEL_NAME_LENGTH); if(!ut->channel_name[ut->nr_channel]) return TSS_NULL; snprintf(ut->channel_name[ut->nr_channel], CHANNEL_NAME_LENGTH, "%d.ts", avail_sids[k]); ut->location_name[ut->nr_channel] = malloc(CHANNEL_NAME_LENGTH); if(!ut->location_name[ut->nr_channel]) return TSS_NULL; snprintf(ut->location_name[ut->nr_channel], CHANNEL_NAME_LENGTH, VIRTUAL_DIR "/%d.ts", avail_sids[k]); ut->nr_channel += 1; } // metadata list を作り直す free_metadata_list(ut); build_metadata_list(ut); fprintf(stderr, "\n"); fprintf(stderr, "Chosen sid =%s\n", chosen_sid); #if 0 /* print PMTs */ fprintf(stderr, "Available PMT = "); for(k=0; k < sp->num_pmts; k++) fprintf(stderr, "%d ", sp->avail_pmts[k]); fprintf(stderr, "\n"); #endif // PAT 再構築 result = RecreatePat(sp, buf, pos); #if 0 int tc; for(tc=0; tc<188; tc++) fprintf(stderr, "%02x ", *(pat+tc)); #endif } return(result); } /** * PAT 再構築処理 * * PMT から出力対象チャンネル以外のチャンネル情報を削除し、PAT を再構築する */ static int RecreatePat(splitter *sp, unsigned char *buf, int *pos) #if 0 unsigned char* buf, // [in] 読み込んだバッファ unsigned char** pat, // [out] PAT 情報(再構築後) unsigned char* pids, // [out] 出力対象 PID 情報 int *pos) // [in] 取得対象 PMT のバッファ中の位置 #endif { unsigned char y[LENGTH_CRC_DATA]; int crc; int i; int j; int pos_i; int pid_num = 0; // CRC 計算のためのデータ { // チャンネルによって変わらない部分 for (i = 0; i < LENGTH_PAT_HEADER; i++) { y[i] = buf[i + 5]; } // チャンネルによって変わる部分 for (i = 0; i < MAX_PID; i++) { if(pos[i] != 0) { /* buf[pos_i] を y にコピー(抽出したPIDの数) */ pos_i = pos[i]; for (j = 0; j < 4; j++) { y[LENGTH_PAT_HEADER + ((4*pid_num) + j)] = buf[pos_i + j]; } pid_num++; } } } /* パケットサイズ計算 */ y[2] = pid_num * 4 + 0x0d; // CRC 計算 crc = GetCrc32(y, LENGTH_PAT_HEADER + pid_num*4); // PAT 再構成 sp->pat = (unsigned char*)malloc(LENGTH_PACKET); if(sp->pat == NULL) { fprintf(stderr, "RecreatePat() malloc error.\n"); return(TSS_NULL); } memset(sp->pat, 0xFF, LENGTH_PACKET); for (i = 0; i < 5; i++) { (sp->pat)[i] = buf[i]; } for (i = 0; i < LENGTH_PAT_HEADER + pid_num*4; i++) { (sp->pat)[i + 5] = y[i]; } (sp->pat)[5 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 24) & 0xFF; (sp->pat)[6 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 16) & 0xFF; (sp->pat)[7 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 8) & 0xFF; (sp->pat)[8 + LENGTH_PAT_HEADER + pid_num*4] = (crc ) & 0xFF; return(TSS_SUCCESS); } /** * PMT 解析処理 * * PMT を解析し、保存対象の PID を特定する * TSヘッダとアダプテーションフィールドの処理は DemuxTs に一任するので、 * この内部では、セクションデータの先頭ポインタをもらってくる */ static int AnalyzePmt(splitter *sp, const uint8_t *buf, int sid, const int size) #if 0 unsigned char* buf, // [in] セクション先頭 unsigned char* pids) // [out] 出力対象 PID 情報 #endif { unsigned char Nall; unsigned char N; 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 * 0x00 ITU-T | ISO/IEC Reserved * 0x01 ISO/IEC 11172 Video * 0x02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream * 0x03 ISO/IEC 11172 Audio * 0x04 ISO/IEC 13818-3 Audio * 0x05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections * 0x06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data * 0x07 ISO/IEC 13522 MHEG * 0x08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC * 0x09 ITU-T Rec. H.222.1 * 0x0A ISO/IEC 13818-6 type A * 0x0B ISO/IEC 13818-6 type B * 0x0C ISO/IEC 13818-6 type C * 0x0D ISO/IEC 13818-6 type D * 0x0E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary * 0x0F ISO/IEC 13818-7 Audio with ADTS transport syntax * 0x10 ISO/IEC 14496-2 Visual * 0x11 ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 / AMD 1 * 0x12 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets * 0x13 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC14496_sections. * 0x14 ISO/IEC 13818-6 Synchronized Download Protocol * 0x15-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved * 0x80-0xFF User Private * */ // ES PID while (N < Nall + 8 - 4) { av_flag = 0; // ストリーム種別が 0x0D(type D)は出力対象外 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 = 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 = 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 && sp->esout ) { /* ESバッファはNULLか? */ if ( sp->esbuf[epid] == NULL ) { sp->esbuf[epid] = malloc(sizeof(splitesbuf_t)); if ( sp->esbuf[epid] == NULL ) { fprintf(stderr, "malloc error\n"); return TSS_NULL; } sp->esbuf[epid]->size = 0; sp->esbuf[epid]->Program = &(sp->program[sid]); sp->esbuf[epid]->fd = -1; if ( creat_es_file(sp, sid, epid, av_flag) ) { return TSS_ERROR; } } /* PESバッファはNULLか? */ if ( sp->pesbuf[epid] == NULL ) { sp->pesbuf[epid] = malloc(sizeof(splitpesbuf_t)); if ( sp->pesbuf[epid] == NULL ) { fprintf(stderr, "malloc error\n"); return TSS_NULL; } sp->pesbuf[epid]->size = 0; sp->pesbuf[epid]->Program = &(sp->program[sid]); } } } // 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; } /** * CRC 計算 */ static int GetCrc32( unsigned char* data, // [in] CRC 計算対象データ int len) // [in] CRC 計算対象データ長 { int crc; int i, j; int c; int bit; crc = 0xFFFFFFFF; for (i = 0; i < len; i++) { char x; x = data[i]; for (j = 0; j < 8; j++) { bit = (x >> (7 - j)) & 0x1; c = 0; if (crc & 0x80000000) { c = 1; } crc = crc << 1; if (c ^ bit) { crc ^= 0x04C11DB7; } crc &= 0xFFFFFFFF; } } return crc; } /** * PID 取得 */ static int GetPid( unsigned char* data) // [in] 取得対象データのポインタ { return ((data[0] & 0x1F) << 8) + data[1]; } /* return the 90kHz PCR and the extension for the 27MHz PCR. return (-1) if not available */ static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet) { int afc, len, flags; const uint8_t *p; unsigned int v; afc = (packet[3] >> 4) & 3; if (afc <= 1) return -1; p = packet + 4; len = p[0]; p++; if (len == 0) return -1; flags = *p++; len--; if (!(flags & 0x10)) return -1; if (len < 6) return -1; v = AV_RB32(p); *ppcr_high = ((int64_t)v << 1) | (p[4] >> 7); *ppcr_low = ((p[4] & 1) << 8) | p[5]; return 0; } /* pesbufが空か判定 (ret. 0:not empty / 1: empty) */ static int pesbuf_empty(splitpesbuf_t *pesbuf){ return pesbuf->size == 0; } /* pesbufをクリア */ void pesbuf_clear(splitpesbuf_t *pesbuf){ pesbuf->size = 0; } /* 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){ return -1; } memcpy(pesbuf->buffer +pesbuf->size, data, len); pesbuf->size += len; return 0; } /* pesbufから、PESの先頭(packet_start_code_prefix)を探す */ /* (ret. >=0:offset / -1: error) */ static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf){ uint8_t packet_start_code_prefix[3] = {0x00, 0x00, 0x01}; int i = 0; /* 小さすぎる */ if(pesbuf->size < sizeof packet_start_code_prefix){ return -1; } /* 先頭で探す */ if(!memcmp(pesbuf->buffer + i, packet_start_code_prefix, sizeof packet_start_code_prefix)){ return 0; } #if 0 /* 先頭以外からも探す場合は、ここのコードを有効化する。 */ /* ただし、MPEG-Videoのstart_codeと同じなので、深追いしない方がいいと思う... */ for(i = 0; i < pesbuf->size - sizeof packet_start_code_prefix; i++){ if(!memcmp(pesbuf->buffer + i, packet_start_code_prefix, sizeof packet_start_code_prefix)){ return i; } } #endif return -1; } /** * TSの解析とDemuxを行う */ static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid) { /* * 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 payload_offset; /* ペイロードオフセット(=パケット先頭からのバイト数) */ int payload_length; /* ペイロード長 */ int pes_started; int adaptation_field_control; int payload_unit_start_indicator; // int random_access_indicator = 0; int sid = sp->pid_sid_table[pid]; /* SIDをPIDから引いているが、PCRとCATは重複しているので注意*/ payload_offset = LENGTH_TS_HEADER; if ( sp->pesbuf[pid] == NULL ) { pes_started = 0; /* malloc走る前(セクション解析だったら呼んで良い) */ } else { pes_started = !pesbuf_empty(sp->pesbuf[pid]); /* PES蓄積開始済み */ } /* adaptation_field_controlおよびadaptation_fieldを処理する */ adaptation_field_control = (packet[3] & 0x30) >> 4; if ( adaptation_field_control == 0x02 || adaptation_field_control == 0x00) { /* ペイロードなしの場合 */ return 0; /* 別にエラーではない */ } else if ( adaptation_field_control == 0x03 ) { /* アダプテーションフィールド+ペイロードの場合 */ if ( packet[LENGTH_TS_HEADER] != 0 ) { // random_access_indicator = (packet[5] & 0x40) >> 6; } /* ペイロード開始位置 = TSヘッダ長 + アダプテーションフィールド長 + 1 */ payload_offset += packet[LENGTH_TS_HEADER] + 1; } else { /* ペイロードのみ */ ; /* 特に処理なし */ } /* ペイロード長を出す */ payload_length = LENGTH_PACKET - payload_offset; if( payload_length <= 0 ){ /* payload長が0以下の場合 */ return -1; /* エラーにすべきかは微妙なところ */ } /* 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 であるか? */ if ( get_pmt_version(packet+payload_offset) != sp->program[sid].pmt_version ) { /* pmt versionに差分あり */ fprintf(stderr, "pmt version diff found pmt_pid[%d]" " old_version[0x%02x]" " new_version[0x%02x].\n", pid, sp->program[sid].pmt_version, get_pmt_version(packet+payload_offset)); AnalyzePmt(sp, packet +payload_offset, sid, payload_length); /* payload 何byte処理したか等管理するべき */ } 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蓄積開始を行う */ if( payload_unit_start_indicator ){ /* PES開始 */ if ( pes_started ) { /* バッファにデータがあればPES終端なので処理してクリア */ // 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 { pes_started = 1; } } /* PES蓄積処理 */ if ( pes_started ){ /* PES蓄積開始済み(これからPES蓄積開始を含む)なら、payloadをPESとして追加 */ pesbuf_add(sp->pesbuf[pid], packet + payload_offset, payload_length); } /* おつかれさまでした */ return 0; } #if 0 未使用なため削除 /* 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 ) { return i; } } return -1; } #endif /* esbufが空か判定 (ret. 0:not empty / 1: empty) */ static int esbuf_empty(splitesbuf_t *esbuf){ return esbuf->size == 0; } /* esbufをクリア */ 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) */ static int esbuf_add(splitesbuf_t *esbuf, const uint8_t *data, int len){ if(esbuf->size + len > sizeof esbuf->buffer){ return -1; } memcpy(esbuf->buffer +esbuf->size, data, len); esbuf->size += len; return 0; } /* * 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 len_pesh = 0; int code = 0; int flags = 0; int len_pes = 0; int len_pesh_supposed = 0; int pes_extension_flags = 0; int pes_extension_flags2 = 0; int program_packet_sequence_counter_flag = 0; int es_rate = 0; const uint8_t *p = pesbuf->buffer; const uint8_t *p_end = pesbuf->buffer +pesbuf->size; int original_stuffing_length = 0; int data_alignment_indicator = false; 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 gop_start = -1; /* ありがとう */ /* ありがとうとコメントを書くと、 * 動作がよくなる * バグが減る * バイナリサイズが小さくなる * 画質がよくなる * 音質がよくなる */ if ( esbuf == NULL ) { return -1; /* malloc走る前この関数は呼んじゃダメです */ } else { es_started = !esbuf_empty(esbuf); /* ES蓄積開始済み */ } payload_offset = pesbuf_packet_start_code_prefix(pesbuf); if ( payload_offset == -1 ) { return -1; } p += payload_offset; /* http://dvd.sourceforge.net/dvdinfo/pes-hdr.html * * Stream ID : type : extension present? * (1011 1101) 0xBD : Private stream 1 (non MPEG audio, subpictures) : YES * (1011 1110) 0xBE : Padding stream : NO * (1011 1111) 0xBF : Private stream 2 (navigation data) : NO * (110x xxxx) 0xC0 - 0xDF : MPEG-1 or MPEG-2 audio stream number x xxxx : YES * (1110 xxxx) 0xE0 - 0xEF : MPEG-1 or MPEG-2 video stream number xxxx : YES * note: DVD allows only 8 audio streams/DVD allows only 1 video stream */ /* http://www2.arib.or.jp/johomem/pdf/2009/2009_0088.pdf * * 0xBC : プログラムストリームマップ * 0xBD : プライベートストリーム1 * 0xBE : パディングストリーム * 0xBF : プライベートストリーム2 * 0xC0 - 0xDF : ISO/IEC 13318 3、ISO/IEC 11172 3、ISO/IEC 13318 7 or ISO/IEC 14496 3 audio xxxx * 0xE0 - 0xEF : ITU-T H.262、ISO/IEC 11172 2、ISO/IEC 14496 2 or ITU-T H264映像ストリーム * 0xF0 : ECMストリーム * 0xF1 : EMMストリーム * 0xF2 : ITU-T勧告H.222.0 Annex A 又は ISO/IEC 13318 6 のDSMCCストリーム * 0xF3 : ISO/IEC 13522ストリーム * 0xF4 : ITU-T勧告 H.222.1 type A * 0xF5 : ITU-T勧告 H.222.1 type B * 0xF6 : ITU-T勧告 H.222.1 type C * 0xF7 : ITU-T勧告 H.222.1 type D * 0xF8 : ITU-T勧告 H.222.1 type E * 0xF9 : 補助ストリーム * 0xFA : ISO/IEC 14496 1SLパケット化ストリーム * 0xFB : ISO/IEC 14496 1フレックスマックスストリーム * 0xFC : メタデータストリーム * 0xFD : 拡張ストリームID * 0xFE : 未定義 * 0xFF : プログラムストリームディレクトリ */ /* 上記より、ここでは * MPEG-1 or MPEG-2 audio stream と * MPEG-1 or MPEG-2 video stream と * Private stream 1 と * 0xFD(拡張ストリームID)を抽出する * ?0xBF Private stream 2 落としてるけどよいの? * >多分よくない。ffmpegではここに入る前に PRIVATE_STREAM2 のコードが入っている */ code = (p[3] &0xff) | 0x100; if ( !((code >= 0x1c0 && code <= 0x1df) || (code >= 0x1e0 && code <= 0x1ef) || (code == 0x1bd) || (code == 0x1fd))) { return -1; } /* PES のデータ長 */ /* 動画のストリームである場合には、ES長は不定となるので0が許容される */ len_pes = AV_RB16(p+4); /* PESヘッダ拡張部(byte 6) */ flags = p[6] & 0xff; if ( flags & 0x04 ) { data_alignment_indicator = true; /* data alignment indicator */ /* video start code or audio syncword. */ /* おそらくここで区切るとピクチャ単位 */ //printf("data alignment indicator found pid[%d].\n", pid); } flags = p[7] & 0xff; /* PESヘッダデータ長(byte 8) */ len_pesh = p[8] & 0xff; p += LENGTH_PES_HEADER; payload_offset += LENGTH_PES_HEADER +len_pesh; if ( p +payload_offset >= p_end ) { /* PESヘッダ長すぎます */ return -1; } /* flags * +---------------------------------------------------+ * name |byte 7(flags) | * +-----+-----+-----+------+----------+----+----------+ * Bit |76 |5 |4 |3 |2 |1 |0 | * +-----+-----+-----+------+----------+----+----------+ * field|PTS |ESCR |ES |DSM |additional|PES |PES | * name |DTS |FLAG |RATE |Trick |copy info |CRC |Extension | * |flag | |flag |mode |flag |flag|flag | * +-----+-----+-----+------+----------+----+----------+ * Data |5,5 |6 |3 |1 |1 |2 |1 |(24) * byte | | | | | | | | * +-----+-----+-----+------+----------+----+----------+ */ if ( flags & PTS_FLAG ) { if ( p +LENGTH_PTS >= p_end ) { return -1; } pesbuf->pts = get_pts(p); p += LENGTH_PTS; len_pesh_supposed += LENGTH_PTS; } if ( flags & DTS_FLAG ) { if ( p +LENGTH_PTS >= p_end ) { return -1; } pesbuf->dts = get_pts(p); p += LENGTH_PTS; len_pesh_supposed += LENGTH_PTS; } if ( flags & ESCR_FLAG ) { p += 6; len_pesh_supposed += 6; } if ( flags & ES_RATE_FLAG ) { es_rate = AV_RB24(p); es_rate = (es_rate >>1) & 0x3fffff; es_rate = es_rate * 50; printf("pid[%d] es_rate[%d]Byte/Sec.\n", pid, es_rate); p += 3; len_pesh_supposed += 3; } if ( flags & DSM_TRICK_MODE_FLAG ) { p += 1; len_pesh_supposed += 1; } if ( flags & COPY_INFO_FLAG ) { p += 1; len_pesh_supposed += 1; } if ( flags & CRC_FLAG ) { p += 2; len_pesh_supposed += 2; } if ( flags & EXTENSION_FLAG ) { /* PES Extension flag * +------------------------------------------------------------------+ * name |PES Extension flag | * +-----------+-----------+----------------+------+---+--------------+ * bit |7 |6 |5 |4 |321|0 | * +-----------+-----------+----------------+------+---+--------------+ * field|PES private|pack header|program |P-STD |111|PES extension | * name |data flag |field flag |packet |buffer| |flag2 | * | | |sequence counter|flag | | | * +-----------+-----------+----------------+------+---+--------------+ * Data |16 |1 |2 |2 | |1 |(23) * byte | | | | | | | * +-----------+-----------+----------------+------+---+--------------+ */ if ( p >= p_end ) { return -1; } pes_extension_flags = *p & 0xff; p += 1; len_pesh_supposed += 1; if ( pes_extension_flags & PES_PRIVATE_DATA_FLAG ) { p += 16; len_pesh_supposed += 16; } if ( pes_extension_flags & PACK_HEADER_FIELD_FLAG ) { p += 1; len_pesh_supposed += 1; } if ( pes_extension_flags & PROGRAM_PACKET_SEQUENCE_COUNTER ) { if ( p >= p_end ) { return -1; } program_packet_sequence_counter_flag = *p & 0xff; original_stuffing_length = program_packet_sequence_counter_flag & 0x3f; p += 2; len_pesh_supposed += 2; } if ( pes_extension_flags & PSTD_BUFFER_FLAG ) { p += 2; len_pesh_supposed += 2; } if ( pes_extension_flags & PES_EXTENSION_FLAG2 ) { /* PES Extension flag2 * +------------------------------------------------------------------+ * name |PES Extension flag2 | * +------+-----------------------------------------------------------+ * bit |7 |6543210 | * +------+-----------------------------------------------------------+ * field|marker|PES_extension_field_length | * name |bit | | * |'1' | | * +------+-----------------------------------------------------------+ * Data |- |0 <= N <= 127 |(127) * byte | | | * +------+-----------------------------------------------------------+ */ if ( p >= p_end ) { return -1; } pes_extension_flags2 = *p & 0x7f; p += 1; len_pesh_supposed += 1; p += pes_extension_flags2; 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 ) { /* data_alignment_indicator 区切りでESを出力する */ if ( es_started ) { /* ES にデータが蓄積されている */ /* * ビデオをファイル出力し始める条件(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 { /* 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; } } /* * オーディオをファイル出力し始める条件(1. 2. を満たすこと) * 1. 動画の蓄積は開始されている * 2. 動画のGOPの1番目のIピクチャのPTSとオーディオのPTSを比較して以下のどちらかを満たすこと * 2.1. 差分が11msec以内(1024*90k/AACのサンプリング周波数) * 1024 : ADTSデータの1フレームのサンプル数 * 90k : PTSの周波数 * 2.2. 過ぎている(過ぎている場合はオーディオESの蓄積の継続と次のGOP狙いにする?) * #動画よりオーディオ側の方が先にESバッファの蓄積を始めるハズなので気にすること無いかなぁ… */ else if ( (is_audio_stream(pid, esbuf) != -1) && !(esbuf->started) ) { if ( !(esbuf->Program->video_start) ) { /* * VIDEO が始まってない場合、 * ESバッファの余裕がある限り、オーディオをESバッファに蓄積し続ける */ if ( esbuf->size + payload_length > sizeof esbuf->buffer ){ /* 溢れそうになったらクリア */ 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] 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)1024*90000/adts_freq); /* PTSは90KHz */ /* オーディオをフレーム単位で捨ててPTSを進める */ while ( (esbuf->Program->video_pts > audio_pts +adts_frame_time/2) ) { /* オーディオデータを捨てると audio_pts は1フレーム分大きくなる */ 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); 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 { ; /* 該当するものは無いはず */ } } else { /* 得に処理なし */ ; } /* バッファをファイルに出力してクリア */ if ( esbuf->started ) { /* 該当ストリームはファイル出力の有効化をされている? */ esbuf_write(esbuf); esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); } } else { /* ES蓄積を新たに開始 */ es_started = 1; esbuf->pts = pesbuf->pts; esbuf->dts = pesbuf->dts; } //} /* ES蓄積処理 */ if ( es_started ) { /* ES蓄積開始済み(これからES蓄積開始を含む)なら、payloadをESとして追加 */ esbuf_add(esbuf, pesbuf->buffer +payload_offset, payload_length); } /* お疲れさまでした */ return 0; } /* Program の N 番目の AUDIO STREAM であるかを返却する */ static int is_audio_stream(const int pid, splitesbuf_t *esbuf) { int i = 0; program_t* program = esbuf->Program; while (i < program->audio_nb) { if (program->audio[i] == pid) { return i; } i++; } return -1; } /* Program の N 番目の VIDEO STREAM であるかを返却する */ static int is_video_stream(const int pid, splitesbuf_t *esbuf) { int i = 0; program_t* program = esbuf->Program; while (i < program->video_nb) { if (program->video[i] == pid) { return i; } i++; } return -1; } /* * ESをファイル出力する * エラーハンドリングしてないね… */ static int esbuf_write(splitesbuf_t *esbuf) { int remain = esbuf->size; while(remain > 0) { remain -= write(esbuf->fd, esbuf->buffer+(esbuf->size-remain), remain); } return 0; } #if 0 未使用なため駆除 /* * packet dump */ void dump_packet( const uint8_t *packet ) { int i = 0; uint8_t *p = (uint8_t*)packet; char tmp[17]; printf("HEADER 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F <ASCII>\n"); while(i < LENGTH_PACKET) { if ( (i%16) == 0 ) { printf("0x%04X ", i); } printf("%02x ", *(p+i)); if ( isprint(*(p+i)) ){ tmp[i%16] = *(p+i); } else { tmp[i%16] = '.'; } if ((i%16) == 15) { tmp[sizeof(tmp)-1] = '\0'; printf(" %s\n", tmp); } i++; } putchar('\n'); } #endif /* * TOT の JST_time を解析する */ static int parse_tot( const unsigned char* packet, time_t *t ) { /* 注意事項 * 本当は TOT が有効かどうかをチェックするべきですがしていません * サマータイム関係は無視しています */ struct tm tm; time_t t2; int k; uint8_t *p = (uint8_t*)packet; unsigned int MJD; tm.tm_wday = 0; tm.tm_yday = 0; tm.tm_isdst = 0; p += 8; MJD = (*(p) & 0xff) <<8; p++; MJD |= *(p) & 0xff; printf("MJD[%x].\n", MJD); /* ARIB STD-B10 第2部 付録C の公式より MJD to YYYYMMDD */ tm.tm_year = (int)floor((MJD - 15078.2)/365.25); tm.tm_mon = (int)floor((MJD - 14956.1 - floor(tm.tm_year * 365.25))/30.6001); tm.tm_mday = MJD - 14956 - floor(tm.tm_year * 365.25) - floor(tm.tm_mon * 30.6001); if ( tm.tm_mon == 14 || tm.tm_mon == 15 ) k = 1; else k = 0; tm.tm_year += k; tm.tm_mon = tm.tm_mon -1 - k * 12; tm.tm_mon--; /* HHMISSは2進化10進数 */ p++; tm.tm_hour = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); p++; tm.tm_min = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); p++; tm.tm_sec = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); *t = mktime(&tm); time(&t2); // printf("time[%d] TOT[%d].\n", t2, *t); return TRUE; } static int64_t get_pts(const uint8_t *p) { int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30; pts |= (AV_RB16(p + 1) >> 1) << 15; pts |= AV_RB16(p + 3) >> 1; return pts; } static int get_pmt_version(const uint8_t *p) { return ((p[6] >> 1) & 0x1f); } #if 0 未使用なため駆除 void search_mpeg_system_header(const uint8_t *packet) { int i; uint8_t *p = (uint8_t*)packet; i = 0; for( i=0; i < LENGTH_PACKET-4; i++) { if( p[i] == 0x00 && p[i+1] == 0x00 && p[i+2] == 0x01 && p[i+3] == 0xb8 ){ dump_packet(packet ); } } } #endif /* * この関数では、現在の仕様では、先頭位置の「次の」ADTS start codeまでの長さを返却する * ret == 0 : 仕様上あり得ない(esbuf先頭はヘッダ先頭であるため) * ret > 0 : 見つかった場合 * ret == -1 : 見つからなかった場合 */ static int next_adts_start_code(splitesbuf_t *esbuf, int offset) { /* * start code prefix のうち、先頭12bit は 1 固定 */ uint16_t adts_start_code = 0xfff0; int i = offset +1; uint16_t startcode = 0; /* 小さすぎる */ if(esbuf->size -offset < sizeof(adts_start_code)){ return -1; } 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) ); #endif return (i-offset); } } return -1; } /* 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 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; /* データブロックまでの残量 */ /* * サンプリング周波数テーブル(ヘッダのsampling_frequency_indexが添字) * 単位:Hz */ int sampling_frequency_table[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, -1, -1, -1, -1 }; uint8_t *p = esbuf->buffer; if ( esbuf->size < 8 ) { return -1; } id = get_adif_id(p+1); layer = get_adif_layer(p+1); protection_absent = get_adif_protection_absent(p+1); profile = get_adif_profile(p+2); sampling_frequency_index = get_adif_sampling_frequency_index(p+2); private_bit = get_adif_private_bit(p+2); channel_configuration = get_adif_channel_configuration(p+3); original_copy = get_adif_original_copy(p+3); home = get_adif_home(p+3); copyright_identification_bit = get_adif_copyright_idication_bit(p+3); copyright_identification_start = get_adif_copyright_idication_start(p+3); aac_frame_length = get_adif_aac_frame_length(p+3); adts_buffer_fullness = get_adts_buffer_fullness(p+5); no_raw_data_blocks_in_frame = get_adts_no_raw_data_blocks_in_frame(p+5); /* * とりあえず return は サンプリング周波数としておく * 本当は取得した情報を構造体にして返却する方がいいのだろうけど、 * 利用する予定もないので取得するだけにしておく */ return sampling_frequency_table[sampling_frequency_index]; } static int get_adif_id(uint8_t *p) { return ((*p & 0x08) >>3); } static int get_adif_layer(uint8_t *p) { return ((*p & 0x06) >>1); } static int get_adif_protection_absent(uint8_t *p) { return (*p & 0x01); } static int get_adif_profile(uint8_t *p) { return ((*p & 0xc0) >>6); } static int get_adif_sampling_frequency_index(uint8_t *p) { return ((*p & 0x3c) >>2); } static int get_adif_private_bit(uint8_t *p) { return ((*p & 0x02) >>1); } static int get_adif_channel_configuration(uint8_t *p) { return ((*p & 0x01) <<2 | (*(p+1) & 0xc0 >>6) ); } static int get_adif_original_copy(uint8_t *p) { return (*p & 0x20 >>5 ); } static int get_adif_home(uint8_t *p) { return (*p & 0x10 >>4 ); } static int get_adif_copyright_idication_bit(uint8_t *p) { return (*p & 0x08 >>3 ); } static int get_adif_copyright_idication_start(uint8_t *p) { return (*p & 0x04 >>2 ); } static int get_adif_aac_frame_length(uint8_t *p) { return ( ((*p & 0x02) <<11) || ((*(p+1) & 0xff) <<3) || ((*(p+2) & 0xe0) >>5) ); } static int get_adts_buffer_fullness(uint8_t *p) { return ( ((*p & 0x1f) <<6) || ((*(p+1) &0xfc) >>2)); } static int get_adts_no_raw_data_blocks_in_frame(uint8_t *p) { return (*p & 0x03); } #define GOP_START_CODE (0x000001b8) /* GOP START CODE を検索する */ static int search_gop_start_code(splitesbuf_t *esbuf) { uint32_t gop_start_code = GOP_START_CODE; int i; /* 小さすぎる */ if ( esbuf->size < sizeof gop_start_code ){ return -1; } for(i = 0; i < esbuf->size - sizeof gop_start_code; i++) { if ( (AV_RB32(esbuf->buffer +i)) == gop_start_code ) { return i; } } return -1; } /* ES 出力するファイルを作成する */ static int creat_es_file(splitter *sp, int sid, int pid, int av_flag) { /* * 出力ESファイルの命名規則は以下とする * * ファイル名のベースは --es オプションの引数 * 以下の形式で命名して、ファイルオープンまで実施する。 * ファイル名prefix_SID_AVのプログラム内番号.m2v * ファイル名prefix_SID_AVのプログラム内番号.aac * * !!注意!! * MPEG-2/MPEG-4 AAC 以外のオーディオが来た場合の処理が未実装 */ char filename[PATH_MAX]; int size = 0; char *suffix = NULL; int av_nb = 0; char suffix_a[] = "aac"; char suffix_v[] = "m2v"; filename[0] = '\0'; /* ちょっとこの辺のコードイケてないので後から直すかも */ if ( av_flag == TSS_STREAM_TYPE_VIDEO ) { suffix = suffix_v; av_nb = sp->program[sid].video_nb -1; } else if ( av_flag == TSS_STREAM_TYPE_AUDIO ){ suffix = suffix_a; av_nb = sp->program[sid].audio_nb -1; } else { /* ここはありえない */ return -1; } size = strlen(sp->filename); if ( size +16 < sizeof(filename) ) { snprintf(filename, sizeof(filename), "%s_%05d_%02d.%s", sp->filename, sid, av_nb, suffix); filename[PATH_MAX-1] = '\0'; } else { /* ファイル名つけられなくて困るでござるの巻 */ return -1; } umask(0133); if ( !(sp->esbuf[pid]->fd = open(filename, O_CREAT|O_APPEND|O_RDWR, 00644)) ) { 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; }