view recpt1/tssplitter_lite.c @ 99:3a3f15b063e1

now recpt1 accepts single sid for terrestrial channels. technical summary: - mark pid to record instead of pid to drop - split after decoding for now
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Fri, 12 Feb 2010 21:40:21 +0900
parents 3fd15032fd3a
children aeba1988234f
line wrap: on
line source

/* 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"

/* prototypes */
static char** AnalyzeSid(char* sid);
static int ReadTs( unsigned char** pat, unsigned char* pids, char** sid_list, unsigned char* pmt_pids, ARIB_STD_B25_BUFFER *sbuf, int* pmt_remain, int* pmt_counter);
static int AnalyzePat(unsigned char* buf, unsigned char** pat, unsigned char* pids, char** sid_list, unsigned char* pmt_pids, int* pmt_remain);
static int RecreatePat(unsigned char* buf, unsigned char** pat, unsigned char* pids, int *pos);
static int AnalyzePmt(unsigned char* buf, unsigned char* pids);
static int GetCrc32(unsigned char* data, int len);
static int GetPid(unsigned char* data);

/**
 * サービス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(引数で指定した文字列)
)
{
	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_remain = -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_remain), &(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 の抽出を行う
 */
static 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_remain,						// [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_remain);
			if (TSS_SUCCESS != result)
			{
				/* 下位の関数内部でmalloc error発生 */
				return result;
			}
		}

		// PMT
		/* 残すpmt_pidである場合には、pmtに書かれている
		 * 残すべきPCR/AUDIO/VIDEO PIDを取得する */
		if (pmt_pids[pid] == 1)
		{
//			fprintf(stderr, "\npmt_pid=%d\n", pid);
			/* この中にはPMT毎に一度しか入らないようにしておく */
			AnalyzePmt(((unsigned char*)sbuf->data)+index, pids);
			pmt_pids[pid]++;
			*pmt_counter += 1;
		}
		/* 録画する全てのPMTについて、中にあるPCR/AUDIO/VIDEOのPIDを
		 * 得る */
		/* pmt_counter と pmt_remain が一致する場合に条件は満たされる */
		if ((*pmt_counter == *pmt_remain)) {
			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
	splitbuf_t *dbuf							// [out]	出力TS
)
{
	int pid;
	unsigned char *sptr, *dptr;
	int s_offset = 0;
	int d_offset = 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);
        switch(pid) {

		// PAT
		case 0x0000:
			// 巡回カウンタカウントアップ
			if(0xFF == splitter->pat_count) {
				splitter->pat_count = splitter->pat[3];
			}
			else {
				splitter->pat_count = (splitter->pat_count + 1) % 16;
			}
			splitter->pat[3] = splitter->pat_count;

			memcpy(dptr + d_offset, splitter->pat, LENGTH_PACKET);
			d_offset += LENGTH_PACKET;
			dbuf->size += LENGTH_PACKET;
			break;
		default:
			/* pids[pid] が 1 は残すパケットなので書き込む */
			if(1 == splitter->pids[pid]) {
				memcpy(dptr + d_offset, sptr + s_offset, LENGTH_PACKET);
				d_offset += LENGTH_PACKET;
				dbuf->size += LENGTH_PACKET;
			}
			break;
		} /* switch */

		s_offset += LENGTH_PACKET;
	}

	return(TSS_SUCCESS);
}

/**
 * PAT 解析処理
 *
 * PAT を解析し、出力対象チャンネルが含まれているかチェックを行い、PAT を再構築する
 */
static 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_remain
)						// [out]	落とすPMTの数
{
	int pos[MAX_PID];
	int service_id;
	int i, j=0, k;
	int size = 0;
	int pid;
	int result = TSS_SUCCESS;
	char **p;
	int sid_found;
	int avail_sids[MAX_SERVICES];

	if(*pat == NULL) {
		/* 初期化 */
		*pmt_remain = 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];
			avail_sids[j] = service_id;
			p = sid_list;

			while(*p) {
				if(service_id == atoi(*p)) {
					/* 録画対象 = 落とす対象とはしないものなので、基本的に何もしない */
					/* 録画対象の pmt_pids は 1 とする */
					/* 録画対象の pmt の pids は 1 とする */
					pid = GetPid(&buf[i + 2]);
					*(pmt_pids+pid) = 1;
					*(pids+pid) = 1;
					pos[pid] = i;
					sid_found = 1;
					*pmt_remain += 1;
//					fprintf(stderr, "sid=%d pid=%d\n", service_id, pid);
					break;
				}
				else if(strstr(*p, "all")) {
					/* all指定時には全保存する */
					pid = GetPid(&buf[i + 2]);
					*(pmt_pids+pid) = 1;
					*(pids+pid) = 1;
					pos[pid] = i;
					sid_found = 1;
					*pmt_remain += 1;
//					fprintf(stderr, "sid=%d pid=%d\n", service_id, pid);
					break;
				}

				p++;
			} /* while */

			j++;
		}

		/* print SIDs */
		fprintf(stderr, "Available sid = ");
		for(k=0; k<j; k++)
			fprintf(stderr, "%d ", avail_sids[k]);
		fprintf(stderr, "\n");

		fprintf(stderr, "Chosen sid    = ");
		p = sid_list;
		while(*p != NULL) {
			fprintf(stderr, "%s ", *p);
			p++;
		}
		fprintf(stderr, "\n");

		// PAT 再構築
		result = RecreatePat(buf, pat, pids, 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(
	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 を特定する
 */
static 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;
//    fprintf(stderr, "pcr_pid:%5d", pcr);

	N = ((buf[15] & 0x0F) << 4) + buf[16] + 16 + 1;

	// ES PID
	while (N < Nall + 8 - 4)
	{
		// ストリーム種別が 0x0D(type D)は出力対象外
		if (0x0D != buf[N])
		{
			epid = GetPid(&buf[N + 1]);

			pids[epid] = 1;
//			fprintf(stderr, "%10d", epid);
		}
		N += 4 + (((buf[N + 3]) & 0x0F) << 4) + buf[N + 4] + 1;
	}

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