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];
+}