comparison 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
comparison
equal deleted inserted replaced
95:a201531113ca 96:52f8e081763d
1 /* tssplitter_lite.c -- split TS stream.
2
3 Copyright 2009 querulous
4 Copyright 2010 Naoya OYAMA <naoya.oyama@gmail.com>
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
18
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include "decoder.h"
27 #include "recpt1.h"
28 #include "tssplitter_lite.h"
29
30
31 /**
32 * サービスID解析
33 */
34 char** AnalyzeSid(
35 char* sid) // [in] サービスID(カンマ区切りテキスト)
36 {
37 int i = 0;
38 char** sid_list = NULL;
39 char* p;
40 int CommaNum = 0;
41
42 /* sid は次の形式の引数を許容する */
43 /* 指定無し */
44 /* SID[0] */
45 /* SID[0],SID[1],...,SID[N-1],SID[N] */
46
47 /*カンマの数を数える*/
48 p = sid;
49 while(*p != '\0')
50 {
51 if( *p == C_CHAR_COMMA ){
52 CommaNum++;
53 }
54 p++;
55 }
56
57 /* sid_listの数はカンマの数+2(NULL止めするから) */
58 sid_list = malloc(sizeof(char*)*(CommaNum+2));
59 if ( sid_list == NULL )
60 {
61 fprintf(stderr, "AnalyzeSid() malloc error.\n");
62 return NULL;
63 }
64
65 /* sidが空である場合 */
66 p = sid;
67 if ( strlen(p) == 0 )
68 {
69 sid_list[0] = NULL;
70 return sid_list;
71 }
72
73 /* カンマ無し */
74 if ( CommaNum == 0 )
75 {
76 sid_list[0] = sid;
77 sid_list[1] = NULL;
78 return sid_list;
79 }
80
81 /* カンマ区切りで複数指定時 */
82 i=0;
83 p = sid;
84 /* 文字列端に到達するか、カンマ数が数えた数に達したら終了 */
85 while((*p != '\0') || i < CommaNum)
86 {
87 /* 現在の処理位置をsid_list[i]にセット */
88 /* このタイミングの p は
89 * ・sid先頭
90 * ・[,]の次の文字
91 * いずれかであるので p を sid_list[i] に代入してよい
92 */
93 sid_list[i] = p;
94 i++;
95
96 /* 最初に現れる[,]をNULL文字で置換する */
97 p = strchr(p, C_CHAR_COMMA);
98 if ( p == NULL )
99 {
100 /* カンマが見つからない場合は最後の処理対象なので終了 */
101 break;
102 }
103 *p = '\0';
104 /* 処理位置をNULLで置換した文字の次の位置に設定する */
105 p++;
106 }
107
108 /* 最後のsid_list[n]はNULLポインタで止める */
109 sid_list[i] = NULL;
110
111 i=0;
112 while( sid_list[i] != NULL )
113 {
114 i++;
115 }
116 for(i=0; sid_list[i] != NULL; i++)
117 {
118 printf("sid_list[%d]=[%s].\n",i, sid_list[i]);
119 }
120 return sid_list;
121 }
122
123 /**
124 * 初期化処理
125 */
126 splitter* split_startup(
127 char *sid // [in] サービスID(引数で指定した文字列)
128 )
129 {
130 splitter* sp;
131 sp = malloc(sizeof(splitter));
132 if ( sp == NULL )
133 {
134 fprintf(stderr, "split_startup malloc error.\n");
135 return NULL;
136 }
137 memset(sp->pids, 0, sizeof(sp->pids));
138 memset(sp->pmt_pids, 0, sizeof(sp->pmt_pids));
139
140 sp->sid_list = NULL;
141 sp->pat = NULL;
142 sp->sid_list = AnalyzeSid(sid);
143 if ( sp->sid_list == NULL )
144 {
145 free(sp);
146 return NULL;
147 }
148 sp->pat_count = 0xFF;
149 sp->pmt_drop = -1;
150 sp->pmt_counter = 0;
151
152 return sp;
153 }
154
155 /**
156 * 落とすPIDを確定させる
157 */
158 int split_select(
159 splitter *sp, // [in/out] splitter構造体
160 ARIB_STD_B25_BUFFER *sbuf // [in] 入力TS
161 )
162 {
163 int result;
164 // TS解析
165 result = ReadTs(&(sp->pat), sp->pids, sp->sid_list, sp->pmt_pids, sbuf, &(sp->pmt_drop), &(sp->pmt_counter));
166
167 return result;
168 }
169
170 /**
171 * 終了処理
172 */
173 void split_shutdown(
174 splitter* sp
175 )
176 {
177 if ( sp != NULL ) {
178 if ( sp->pat != NULL )
179 {
180 free(sp->pat);
181 sp->pat = NULL;
182 }
183 if ( sp->sid_list != NULL )
184 {
185 free(sp->sid_list);
186 sp->sid_list = NULL;
187 }
188 free(sp);
189 sp = NULL;
190 }
191 }
192
193 /**
194 * TS 解析処理
195 *
196 * 対象のチャンネル番号のみの PAT の再構築と出力対象 PID の抽出を行う
197 */
198 int ReadTs(
199 unsigned char** pat, // [out] PAT 情報(再構築後)
200 unsigned char* pids, // [out] 出力対象 PID 情報
201 char** sid_list, // [in] 出力対象サービス ID のリスト
202 unsigned char* pmt_pids, // [in] 出力対象PIDのPMT PID
203 ARIB_STD_B25_BUFFER *sbuf, // [in] pt1_drvの入力TS
204 int* pmt_drop, // [in] PMTの落とすべき数
205 int* pmt_counter // [out] PMTの落とした数
206 )
207 {
208 int length = sbuf->size;
209 int pid;
210 int result = TSS_ERROR;
211 int index;
212
213 index = 0;
214 while (((length - index - LENGTH_PACKET)) > 0)
215 {
216 pid = GetPid(((unsigned char*)sbuf->data)+index+1);
217 // PAT
218 if (0x0000 == pid)
219 {
220 result = AnalyzePat(((unsigned char*)sbuf->data)+index, pat, pids, sid_list, pmt_pids, pmt_drop);
221 if (TSS_SUCCESS != result)
222 {
223 /* 下位の関数内部でmalloc error発生 */
224 return result;
225 }
226 }
227
228 // PMT
229 /* 落とすpmt_pidである場合には、pmtに書かれている
230 * 落とすべきPCR/AUDIO/VIDEO PIDを取得する */
231 if (pmt_pids[pid] == 1)
232 {
233 /* この中にはPMT毎に一度しか入らないようにしておく */
234 AnalyzePmt(((unsigned char*)sbuf->data)+index, pids);
235 pmt_pids[pid]++;
236 *pmt_counter = *(pmt_counter)+1;
237 }
238 /* 全ての落とすPMTの中に書かれている、落とすPCR/AUDIO/VIDEOのPIDを得たら処理を抜ける */
239 /* pmt_counter と pmt_drop が一致する場合に条件は満たされる */
240 if ((*pmt_counter == *pmt_drop)) {
241 result = TSS_SUCCESS;
242 break;
243 }
244 else
245 {
246 result = TSS_ERROR;
247 }
248 index += LENGTH_PACKET;
249 }
250
251 return(result);
252 }
253
254 /**
255 * TS 分離処理
256 */
257 int split_ts(
258 splitter *splitter, // [in] splitterパラメータ
259 ARIB_STD_B25_BUFFER *sbuf, // [in] 入力TS
260 BUFSZ *dbuf // [out] 出力TS
261 )
262 {
263 int pid;
264 int s_offset = 0;
265 int d_offset = 0;
266
267 /* 初期化 */
268 dbuf->size = 0;
269 if ( sbuf->size < 0 )
270 {
271 return TSS_ERROR;
272 }
273
274 while (sbuf->size > s_offset)
275 {
276 pid = GetPid(((unsigned char*)sbuf->data)+s_offset+1);
277
278 // PAT
279 if (0x0000 == pid)
280 {
281 // 巡回カウンタカウントアップ
282 if (0xFF == splitter->pat_count)
283 {
284 splitter->pat_count = splitter->pat[3];
285 } else
286 {
287 splitter->pat_count = (splitter->pat_count)+1;
288 if (0 == splitter->pat_count % 0x10)
289 {
290 splitter->pat_count = splitter->pat_count - 0x10;
291 }
292 }
293 splitter->pat[3] = splitter->pat_count;
294
295 memcpy(((unsigned char*)dbuf->buffer)+d_offset, splitter->pat, LENGTH_PACKET);
296 d_offset += LENGTH_PACKET;
297 dbuf->size = dbuf->size + LENGTH_PACKET;
298 }
299
300 // その他 PID
301 else
302 {
303 /* pids[pid] が 0 は落とさないパケットなので書き込む */
304 if (0 == splitter->pids[pid])
305 {
306 memcpy(((unsigned char*)dbuf->buffer)+d_offset, ((unsigned char*)sbuf->data)+s_offset, LENGTH_PACKET);
307 d_offset += LENGTH_PACKET;
308 dbuf->size = dbuf->size + LENGTH_PACKET;
309 }
310 }
311 s_offset = s_offset + LENGTH_PACKET;
312 }
313
314 return(TSS_SUCCESS);
315 }
316
317 /**
318 * PAT 解析処理
319 *
320 * PAT を解析し、出力対象チャンネルが含まれているかチェックを行い、PAT を再構築する
321 */
322 int AnalyzePat(
323 unsigned char* buf, // [in] 読み込んだバッファ
324 unsigned char** pat, // [out] PAT 情報(再構築後)
325 unsigned char* pids, // [out] 出力対象 PID 情報
326 char** sid_list, // [in] 出力対象サービス ID のリスト
327 unsigned char* pmt_pids, // [out] サービス ID に対応する PMT の PID
328 int* pmt_drop) // [out] 落とすPMTの数
329 {
330 int pos[MAX_PID];
331 int service_id;
332 int i;
333 int size = 0;
334 int pid;
335 int result = TSS_SUCCESS;
336 char **p;
337 int sid_found;
338
339 if (NULL == *pat)
340 {
341 /* 初期化 */
342 *pmt_drop = 0;
343 memset(pos, 0, sizeof(pos));
344 size = buf[7];
345
346 // 対象チャンネル判定
347 /* size + 8 = パケット全長 */
348 /* 最終 4 バイトはCRCなので飛ばす */
349 for (i = 17; i < (size + 8) - 4; i = i + 4)
350 {
351 sid_found = 0;
352 service_id = (buf[i] << 8) + buf[i + 1];
353 p = sid_list;
354 while(*p != NULL)
355 {
356 if (service_id == atoi(*p))
357 {
358 /* 録画対象 = 落とす対象とはしないものなので、基本的に何もしない */
359 /* 録画対象の pmt_pids は 0 とする */
360 /* 録画対象の pmt の pids は 0 とする */
361 pid = GetPid(&buf[i + 2]);
362 *(pmt_pids+pid) = 0;
363 *(pids+pid) = 0;
364 pos[pid] = i;
365 sid_found = 1;
366 break;
367 }
368 else if (strstr(*p, "all") != NULL )
369 {
370 /* all指定時には全保存する */
371 pid = GetPid(&buf[i + 2]);
372 *(pmt_pids+pid) = 0;
373 *(pids+pid) = 0;
374 pos[pid] = i;
375 sid_found = 1;
376 break;
377 }
378 p++;
379 }
380 if ( ! sid_found )
381 {
382 /* sid_list を全部なめたが録画対象であると判定されないものを落とす */
383 /* 削除対象の pmt_pids は 1 とする */
384 /* 削除対象の pmt の pids は 1 とする */
385 pid = GetPid(&buf[i + 2]);
386 *(pmt_pids+pid) = 1;
387 *(pids+pid) = 1;
388 pos[pid] = i;
389 *(pmt_drop) = *(pmt_drop)+1;
390 }
391 }
392 // PAT 再構築
393 result = RecreatePat(buf, pat, pids, pos);
394 }
395
396 return(result);
397 }
398
399 /**
400 * PAT 再構築処理
401 *
402 * PMT から出力対象チャンネル以外のチャンネル情報を削除し、PAT を再構築する
403 */
404 int RecreatePat(
405 unsigned char* buf, // [in] 読み込んだバッファ
406 unsigned char** pat, // [out] PAT 情報(再構築後)
407 unsigned char* pids, // [out] 出力対象 PID 情報
408 int *pos) // [in] 取得対象 PMT のバッファ中の位置
409 {
410 unsigned char y[LENGTH_CRC_DATA];
411 int crc;
412 int i;
413 int j;
414 int pos_i;
415 int pid_num = 0;
416
417 // CRC 計算のためのデータ
418 {
419 // チャンネルによって変わらない部分
420 for (i = 0; i < LENGTH_PAT_HEADER; i++)
421 {
422 y[i] = buf[i + 5];
423 }
424 // チャンネルによって変わる部分
425 for (i = 0; i < MAX_PID; i++)
426 {
427 if(pos[i] != 0)
428 {
429 /* buf[pos_i] を y にコピー(抽出したPIDの数) */
430 pos_i = pos[i];
431 for (j = 0; j < 4; j++)
432 {
433 y[LENGTH_PAT_HEADER + ((4*pid_num) + j)] = buf[pos_i + j];
434 }
435 pid_num++;
436 }
437 }
438 }
439 /* パケットサイズ計算 */
440 y[2] = pid_num * 4 + 0x0d;
441 // CRC 計算
442 crc = GetCrc32(y, LENGTH_PAT_HEADER + pid_num*4);
443
444 // PAT 再構成
445 *pat = (unsigned char*)malloc(LENGTH_PACKET);
446 if ( *pat == NULL )
447 {
448 fprintf(stderr, "RecreatePat() malloc error.\n");
449 return(TSS_NULL);
450 }
451 memset(*pat, 0xFF, LENGTH_PACKET);
452 for (i = 0; i < 5; i++)
453 {
454 (*pat)[i] = buf[i];
455 }
456 for (i = 0; i < LENGTH_PAT_HEADER + pid_num*4; i++)
457 {
458 (*pat)[i + 5] = y[i];
459 }
460 (*pat)[5 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 24) & 0xFF;
461 (*pat)[6 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 16) & 0xFF;
462 (*pat)[7 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 8) & 0xFF;
463 (*pat)[8 + LENGTH_PAT_HEADER + pid_num*4] = (crc ) & 0xFF;
464 return(TSS_SUCCESS);
465 }
466
467 /**
468 * PMT 解析処理
469 *
470 * PMT を解析し、削除対象の PID を特定する
471 */
472 int AnalyzePmt(
473 unsigned char* buf, // [in] 読み込んだバッファ
474 unsigned char* pids) // [out] 出力対象 PID 情報
475 {
476 unsigned char Nall;
477 unsigned char N;
478 int pcr;
479 int epid;
480
481 Nall = ((buf[6] & 0x0F) << 4) + buf[7];
482
483 // PCR
484 pcr = GetPid(&buf[13]);
485 pids[pcr] = 1;
486
487 N = ((buf[15] & 0x0F) << 4) + buf[16] + 16 + 1;
488
489 // ES PID
490 while (N < Nall + 8 - 4)
491 {
492 epid = GetPid(&buf[N + 1]);
493
494 pids[epid] = 1;
495 N += 4 + (((buf[N + 3]) & 0x0F) << 4) + buf[N + 4] + 1;
496 }
497
498 return TSS_SUCCESS;
499 }
500
501 /**
502 * CRC 計算
503 */
504 int GetCrc32(
505 unsigned char* data, // [in] CRC 計算対象データ
506 int len) // [in] CRC 計算対象データ長
507 {
508 int crc;
509 int i, j;
510 int c;
511 int bit;
512
513 crc = 0xFFFFFFFF;
514 for (i = 0; i < len; i++)
515 {
516 char x;
517 x = data[i];
518
519 for (j = 0; j < 8; j++)
520 {
521
522 bit = (x >> (7 - j)) & 0x1;
523
524 c = 0;
525 if (crc & 0x80000000)
526 {
527 c = 1;
528 }
529
530 crc = crc << 1;
531
532 if (c ^ bit)
533 {
534 crc ^= 0x04C11DB7;
535 }
536
537 crc &= 0xFFFFFFFF;
538 }
539 }
540
541 return crc;
542 }
543
544 /**
545 * PID 取得
546 */
547 int GetPid(
548 unsigned char* data) // [in] 取得対象データのポインタ
549 {
550 return ((data[0] & 0x1F) << 8) + data[1];
551 }