/*
* @(#)
*
* Copyright (c) 2002-2005 by dvb.matt, All Rights Reserved.
*
* This file is part of ProjectX, a free Java based demux utility.
* By the authors, ProjectX is intended for educational purposes only,
* as a non-commercial test project.
*
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package net.sourceforge.dvb.projectx.parser;
import java.io.IOException;
import java.util.ArrayList;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.parser.CommonParsing;
public class TS_PMTParser extends Object {
private int last_version_number = -1;
private int service_id = -1;
private int pmt_pid = -1;
private int pcr_pid = -1;
private int pid_mask = 0x1FFF;
private int pid_mask_inv = 0xE000;
private int length_mask = 0xFFF;
private int length_mask_inv = 0xF000;
private ArrayList video_streams = null; // more than one
private ArrayList audio_streams = null; // more than one
private ArrayList teletext_streams = null; // more than one
private ArrayList subtitle_streams = null; // more than one
/**
*
*/
public TS_PMTParser(int _service_id, int _pmt_pid)
{
pmt_pid = _pmt_pid;
service_id = _service_id;
//Common.setMessage("XX " + pmt_pid + " / " + service_id);
}
/**
*
*/
public int getPID()
{
return pmt_pid;
}
/**
*
*/
public int getServiceID()
{
return service_id;
}
/**
*
*/
public int parsePMT(byte[] packet)
{
if (packet.length < 6)
return -1;
//String str = "";
//for (int i = 0; i < packet.length; i++)
// str += " " + Common.adaptString(Integer.toHexString(0xFF & packet[i]), 2);
//Common.setMessage("FF " + str);
int table_id = getValue(packet, 0, 2);
int tmp_value = getValue(packet, 2, 1);
//id + marker
if (table_id != 2 || (0xF0 & tmp_value) != 0xB0)
return -2;
int section_length = length_mask & getValue(packet, 2, 2);
if (section_length > packet.length - 4)
section_length = packet.length - 4;
int program_number = getValue(packet, 4, 2);
if (service_id != program_number)
return -3;
tmp_value = getValue(packet, 6, 1);
//marker
if ((0xC0 & tmp_value) != 0xC0)
return -4;
int version_number = 0x1F & tmp_value>>1;
if (last_version_number >= 0 && last_version_number == version_number) // 0 .. 31, +1 whenever a change occur
return -5;
int current_next_indicator = tmp_value & 1;
if (current_next_indicator == 0) // 1 = current applicable table, 0 announces next
return -6;
int section_number = getValue(packet, 7, 1); // shall be 0
int last_section_number = getValue(packet, 8, 1); // shall be 0
tmp_value = getValue(packet, 9, 2);
//marker
if ((tmp_value & pid_mask_inv) != pid_mask_inv)
return -7;
pcr_pid = tmp_value & pid_mask;
tmp_value = getValue(packet, 11, 2);
//marker
if ((tmp_value & length_mask_inv) != length_mask_inv)
return -8;
int program_info_length = length_mask & tmp_value;
int packet_offset = 13 + program_info_length;
for (int streamtype, pid; packet_offset < section_length + 4 && packet_offset < packet.length; )
{
streamtype = getValue(packet, packet_offset++, 1);
tmp_value = getValue(packet, (packet_offset += 2), 2);
//marker
if ((tmp_value & pid_mask_inv) != pid_mask_inv)
continue;
pid = pid_mask & tmp_value;
tmp_value = getValue(packet, (packet_offset += 2), 2);
//marker
if ((tmp_value & length_mask_inv) != length_mask_inv)
continue;
program_info_length = length_mask & tmp_value;
switch(streamtype)
{
case 0x01: //mpv1
case 0x02: //mpv2
getDescriptor(packet, packet_offset, program_info_length, pid, 2);
break;
case 0x03: //mpa1
case 0x04: //mpa2
getDescriptor(packet, packet_offset, program_info_length, pid, 4);
break;
case 0x1B: //h264
getDescriptor(packet, packet_offset, program_info_length, pid, 0x1B);
break;
case 0x80:
case 0x81: //private data of AC3 in ATSC
case 0x82:
case 0x83:
case 0x06: //private data in DVB
getDescriptor(packet, packet_offset, program_info_length, pid, 6);
break;
}
packet_offset += program_info_length;
}
last_version_number = version_number;
return 0;
}
/**
*
*/
private void getDescriptor(byte check[], int off, int len, int pid, int type)
{
String str = "";
int chunk_end = 0;
int end = off + len;
try
{
loop:
for (; off < end && off < check.length; off++)
{
switch(0xFF & check[off])
{
case 0x59: //dvb subtitle descriptor
type = 0x59;
chunk_end = off + 2 + (0xFF & check[off+1]);
str += "(";
for (int a=off+2; a<chunk_end; a+=8)
{
for (int b=a; b<a+3; b++) //language
str += (char)(0xFF & check[b]);
int page_type = 0xFF & check[a+3];
int comp_page_id = (0xFF & check[a+4])<<16 | (0xFF & check[a+5]);
int anci_page_id = (0xFF & check[a+6])<<16 | (0xFF & check[a+7]);
str += "_0x" + Integer.toHexString(page_type).toUpperCase();
str += "_p" + comp_page_id;
str += "_a" + anci_page_id + " ";
}
str += ")";
break loop;
case 0x56: //teletext descriptor incl. index page + subtitle pages
type = 0x56;
chunk_end = off + 2 + (0xFF & check[off+1]);
str += "(";
for (int a=off+2; a<chunk_end; a+=5)
{
for (int b=a; b<a+3; b++) //language
str += (char)(0xFF & check[b]);
int page_type = (0xF8 & check[a+3])>>>3;
int page_number = 0xFF & check[a+4];
str += "_";
switch (page_type)
{
case 1:
str += "i";
break;
case 2:
str += "s";
break;
case 3:
str += "ai";
break;
case 4:
str += "ps";
break;
case 5:
str += "s.hip";
break;
default:
str += "res";
}
str += Integer.toHexString((7 & check[a+3]) == 0 ? 8 : (7 & check[a+3])).toUpperCase();
str += (page_number < 0x10 ? "0" : "") + Integer.toHexString(page_number).toUpperCase() + " ";
}
str += ")";
//break loop;
off++;
off += (0xFF & check[off]);
break;
case 0xA: //ISO 639 language descriptor
str += "(";
for (int a=off+2; a<off+5; a++)
str += (char)(0xFF & check[a]);
str += ")";
off++;
off += (0xFF & check[off]);
break;
case 0x6A: //ac3 descriptor
str += "(AC-3)";
off++;
off += (0xFF & check[off]);
break;
case 0xC3: //VBI descriptor
off++;
switch (0xFF & check[off + 1])
{
case 4:
str += "(VPS)";
type = 0xC3;
break;
case 5:
str += "(WSS)";
break;
case 6:
str += "(CC)";
break;
case 1:
str += "(EBU-TTX)";
break;
case 7:
str += "(VBI)";
break;
}
off += (0xFF & check[off]);
break;
case 0x52: //ID of service
chunk_end = off + 2 + (0xFF & check[off+1]);
str += "(#" + (0xFF & check[off + 2]) + ")";
off++;
off += (0xFF & check[off]);
break;
case 0x6B: //ancillary desc
chunk_end = off + 2 + (0xFF & check[off + 1]);
str += "(RDS)";
off++;
off += (0xFF & check[off]);
break;
case 0x5: //registration descriptor
chunk_end = off + 2 + (0xFF & check[off+1]);
str += "(";
for (int a=off+2; a<chunk_end; a++)
str += (char)(0xFF & check[a]);
str += ")";
default:
off++;
off += (0xFF & check[off]);
}
}
String out = "PID: 0x" + Integer.toHexString(pid).toUpperCase();
switch (type)
{
case 0x59:
// subtitle_streams.add(out + str);
subtitle_streams.add(String.valueOf(pid));
break;
case 0x56:
// teletext_streams.add(out + str);
teletext_streams.add(String.valueOf(pid));
break;
case 0x1B:
// video_streams.add(out + str + "(H.264)");
break;
case 2:
// video_streams.add(out + str);
video_streams.add(String.valueOf(pid));
break;
case 0xC3:
// video_streams.add(out + str);
break;
case 4:
// audio_streams.add(out + str);
audio_streams.add(String.valueOf(pid));
break;
default:
// audio_streams.add(out + str + "[PD]");
audio_streams.add(String.valueOf(pid));
}
}
catch (ArrayIndexOutOfBoundsException ae)
{
//playtime += msg_6;
}
}
private int getValue(byte[] array, int offset, int length)
{
int value = 0;
for (int i = 0, j = offset + length - 1; i < length; i++)
value |= (0xFF & array[j - i])<<(i * 8);
return value;
}
}