Mercurial > pt1.oyama
diff recpt1/tssplitter_lite.c @ 96:52f8e081763d
add an option to specify the necessary service IDs.
patch by Naoya OYAMA <naoya.oyama@gmail.com>, based on the code of tssplitter_lite by querulous.
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Wed, 10 Feb 2010 14:33:32 +0900 |
parents | |
children | 3fd15032fd3a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/recpt1/tssplitter_lite.c Wed Feb 10 14:33:32 2010 +0900 @@ -0,0 +1,551 @@ +/* tssplitter_lite.c -- split TS stream. + + Copyright 2009 querulous + Copyright 2010 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 "decoder.h" +#include "recpt1.h" +#include "tssplitter_lite.h" + + +/** + * サービスID解析 + */ +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++; + } + for(i=0; sid_list[i] != NULL; i++) + { + printf("sid_list[%d]=[%s].\n",i, sid_list[i]); + } + return sid_list; +} + +/** + * 初期化処理 + */ +splitter* split_startup( + char *sid // [in] サービスID(引数で指定した文字列) +) +{ + splitter* sp; + sp = malloc(sizeof(splitter)); + if ( sp == 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)); + + 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_drop = -1; + sp->pmt_counter = 0; + + return sp; +} + +/** + * 落とすPIDを確定させる + */ +int split_select( + splitter *sp, // [in/out] splitter構造体 + ARIB_STD_B25_BUFFER *sbuf // [in] 入力TS +) +{ + int result; + // TS解析 + result = ReadTs(&(sp->pat), sp->pids, sp->sid_list, sp->pmt_pids, sbuf, &(sp->pmt_drop), &(sp->pmt_counter)); + + return result; +} + +/** + * 終了処理 + */ +void split_shutdown( + splitter* sp +) +{ + if ( sp != NULL ) { + if ( sp->pat != NULL ) + { + free(sp->pat); + sp->pat = NULL; + } + if ( sp->sid_list != NULL ) + { + free(sp->sid_list); + sp->sid_list = NULL; + } + free(sp); + sp = NULL; + } +} + +/** + * TS 解析処理 + * + * 対象のチャンネル番号のみの PAT の再構築と出力対象 PID の抽出を行う + */ +int ReadTs( + unsigned char** pat, // [out] PAT 情報(再構築後) + unsigned char* pids, // [out] 出力対象 PID 情報 + char** sid_list, // [in] 出力対象サービス ID のリスト + unsigned char* pmt_pids, // [in] 出力対象PIDのPMT PID + ARIB_STD_B25_BUFFER *sbuf, // [in] pt1_drvの入力TS + int* pmt_drop, // [in] PMTの落とすべき数 + int* pmt_counter // [out] PMTの落とした数 +) +{ + int length = sbuf->size; + int pid; + int result = TSS_ERROR; + int index; + + index = 0; + while (((length - index - LENGTH_PACKET)) > 0) + { + pid = GetPid(((unsigned char*)sbuf->data)+index+1); + // PAT + if (0x0000 == pid) + { + result = AnalyzePat(((unsigned char*)sbuf->data)+index, pat, pids, sid_list, pmt_pids, pmt_drop); + if (TSS_SUCCESS != result) + { + /* 下位の関数内部でmalloc error発生 */ + return result; + } + } + + // PMT + /* 落とすpmt_pidである場合には、pmtに書かれている + * 落とすべきPCR/AUDIO/VIDEO PIDを取得する */ + if (pmt_pids[pid] == 1) + { + /* この中にはPMT毎に一度しか入らないようにしておく */ + AnalyzePmt(((unsigned char*)sbuf->data)+index, pids); + pmt_pids[pid]++; + *pmt_counter = *(pmt_counter)+1; + } + /* 全ての落とすPMTの中に書かれている、落とすPCR/AUDIO/VIDEOのPIDを得たら処理を抜ける */ + /* pmt_counter と pmt_drop が一致する場合に条件は満たされる */ + if ((*pmt_counter == *pmt_drop)) { + result = TSS_SUCCESS; + break; + } + else + { + result = TSS_ERROR; + } + index += LENGTH_PACKET; + } + + return(result); +} + +/** + * TS 分離処理 + */ +int split_ts( + splitter *splitter, // [in] splitterパラメータ + ARIB_STD_B25_BUFFER *sbuf, // [in] 入力TS + BUFSZ *dbuf // [out] 出力TS +) +{ + int pid; + int s_offset = 0; + int d_offset = 0; + + /* 初期化 */ + dbuf->size = 0; + if ( sbuf->size < 0 ) + { + return TSS_ERROR; + } + + while (sbuf->size > s_offset) + { + pid = GetPid(((unsigned char*)sbuf->data)+s_offset+1); + + // PAT + if (0x0000 == pid) + { + // 巡回カウンタカウントアップ + if (0xFF == splitter->pat_count) + { + splitter->pat_count = splitter->pat[3]; + } else + { + splitter->pat_count = (splitter->pat_count)+1; + if (0 == splitter->pat_count % 0x10) + { + splitter->pat_count = splitter->pat_count - 0x10; + } + } + splitter->pat[3] = splitter->pat_count; + + memcpy(((unsigned char*)dbuf->buffer)+d_offset, splitter->pat, LENGTH_PACKET); + d_offset += LENGTH_PACKET; + dbuf->size = dbuf->size + LENGTH_PACKET; + } + + // その他 PID + else + { + /* pids[pid] が 0 は落とさないパケットなので書き込む */ + if (0 == splitter->pids[pid]) + { + memcpy(((unsigned char*)dbuf->buffer)+d_offset, ((unsigned char*)sbuf->data)+s_offset, LENGTH_PACKET); + d_offset += LENGTH_PACKET; + dbuf->size = dbuf->size + LENGTH_PACKET; + } + } + s_offset = s_offset + LENGTH_PACKET; + } + + return(TSS_SUCCESS); +} + +/** + * PAT 解析処理 + * + * PAT を解析し、出力対象チャンネルが含まれているかチェックを行い、PAT を再構築する + */ +int AnalyzePat( + 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_drop) // [out] 落とすPMTの数 +{ + int pos[MAX_PID]; + int service_id; + int i; + int size = 0; + int pid; + int result = TSS_SUCCESS; + char **p; + int sid_found; + + if (NULL == *pat) + { + /* 初期化 */ + *pmt_drop = 0; + memset(pos, 0, sizeof(pos)); + size = buf[7]; + + // 対象チャンネル判定 + /* size + 8 = パケット全長 */ + /* 最終 4 バイトはCRCなので飛ばす */ + for (i = 17; i < (size + 8) - 4; i = i + 4) + { + sid_found = 0; + service_id = (buf[i] << 8) + buf[i + 1]; + p = sid_list; + while(*p != NULL) + { + if (service_id == atoi(*p)) + { + /* 録画対象 = 落とす対象とはしないものなので、基本的に何もしない */ + /* 録画対象の pmt_pids は 0 とする */ + /* 録画対象の pmt の pids は 0 とする */ + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 0; + *(pids+pid) = 0; + pos[pid] = i; + sid_found = 1; + break; + } + else if (strstr(*p, "all") != NULL ) + { + /* all指定時には全保存する */ + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 0; + *(pids+pid) = 0; + pos[pid] = i; + sid_found = 1; + break; + } + p++; + } + if ( ! sid_found ) + { + /* sid_list を全部なめたが録画対象であると判定されないものを落とす */ + /* 削除対象の pmt_pids は 1 とする */ + /* 削除対象の pmt の pids は 1 とする */ + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + *(pmt_drop) = *(pmt_drop)+1; + } + } + // PAT 再構築 + result = RecreatePat(buf, pat, pids, pos); + } + + return(result); +} + +/** + * PAT 再構築処理 + * + * PMT から出力対象チャンネル以外のチャンネル情報を削除し、PAT を再構築する + */ +int RecreatePat( + unsigned char* buf, // [in] 読み込んだバッファ + unsigned char** pat, // [out] PAT 情報(再構築後) + unsigned char* pids, // [out] 出力対象 PID 情報 + int *pos) // [in] 取得対象 PMT のバッファ中の位置 +{ + 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 再構成 + *pat = (unsigned char*)malloc(LENGTH_PACKET); + if ( *pat == NULL ) + { + fprintf(stderr, "RecreatePat() malloc error.\n"); + return(TSS_NULL); + } + memset(*pat, 0xFF, LENGTH_PACKET); + for (i = 0; i < 5; i++) + { + (*pat)[i] = buf[i]; + } + for (i = 0; i < LENGTH_PAT_HEADER + pid_num*4; i++) + { + (*pat)[i + 5] = y[i]; + } + (*pat)[5 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 24) & 0xFF; + (*pat)[6 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 16) & 0xFF; + (*pat)[7 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 8) & 0xFF; + (*pat)[8 + LENGTH_PAT_HEADER + pid_num*4] = (crc ) & 0xFF; + return(TSS_SUCCESS); +} + +/** + * PMT 解析処理 + * + * PMT を解析し、削除対象の PID を特定する + */ +int AnalyzePmt( + unsigned char* buf, // [in] 読み込んだバッファ + unsigned char* pids) // [out] 出力対象 PID 情報 +{ + unsigned char Nall; + unsigned char N; + int pcr; + int epid; + + Nall = ((buf[6] & 0x0F) << 4) + buf[7]; + + // PCR + pcr = GetPid(&buf[13]); + pids[pcr] = 1; + + N = ((buf[15] & 0x0F) << 4) + buf[16] + 16 + 1; + + // ES PID + while (N < Nall + 8 - 4) + { + epid = GetPid(&buf[N + 1]); + + pids[epid] = 1; + N += 4 + (((buf[N + 3]) & 0x0F) << 4) + buf[N + 4] + 1; + } + + return TSS_SUCCESS; +} + +/** + * CRC 計算 + */ +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 取得 + */ +int GetPid( + unsigned char* data) // [in] 取得対象データのポインタ +{ + return ((data[0] & 0x1F) << 8) + data[1]; +}