/*
* @(#)Sup2VobSub.java
*
* Copyright (c) 2006 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.subtitle;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.PushbackInputStream;
import java.io.FileOutputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.util.Arrays;
import net.sourceforge.dvb.projectx.xinput.XInputFile;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.parser.CommonParsing;
/**
*
*/
public class Sup2VobSub extends Object {
private PushbackInputStream inputstream;
private BufferedOutputStream outputstream;
private PrintWriter printstream;
private XInputFile xInputFile;
private String outputfile_sub;
private String outputfile_idx;
private long file_length;
private long read_position;
private long write_position;
private long pts_value;
private int buffersize = 12;
private int packetsize = 0xFFFF;
private int packsize = 2048;
private int packetlength;
private int startoffset;
private byte[] sup_packet;
private byte[] export_pack;
private byte[] pack_header = {
0, 0, 1, (byte)0xBA, 0x44, 2, (byte)0xC4, (byte)0x82,
4, (byte)0xA9, 1, (byte)0x89, (byte)0xC3, (byte)0xF8
};
private byte[] pes_header = { 0, 0, 1, (byte)0xBD, 0, 0 };
private byte[] padding_header = { 0, 0, 1, (byte)0xBE, 0, 0 };
private byte[] pes_extension1 = { (byte)0x81, (byte)0x80, 5, 0, 0, 0, 0, 0 };
private byte[] pes_extension2 = { (byte)0x80, 0, 0 };
private byte stream_id = 0x20;
/**
*
*/
private Sup2VobSub()
{}
/**
*
*/
public Sup2VobSub(String file)
{
if (init(file))
parseStream();
}
/**
*
*/
public Sup2VobSub(String file, Object[] colour_table)
{
if (init(file, colour_table))
parseStream();
}
/**
*
*/
private boolean init(String file)
{
return init(file, null);
}
/**
*
*/
private boolean init(String file, Object[] colour_table)
{
boolean b = false;
xInputFile = new XInputFile(new File(file));
file_length = xInputFile.length();
read_position = 0;
write_position = 0;
if (!initStreams())
return b;
initIndex(colour_table);
sup_packet = new byte[packetsize];
export_pack = new byte[packsize];
System.arraycopy(pack_header, 0, export_pack, 0, pack_header.length);
System.arraycopy(pes_header, 0, export_pack, pack_header.length, pes_header.length);
return !b;
}
/**
*
*/
private void parseStream()
{
while (read_position < file_length)
{
while (!nextStartCode())
{}
if (!readPicture())
continue;
writePicture();
}
closeStreams();
}
/**
*
*/
private boolean nextStartCode()
{
boolean b = false;
startoffset = 0;
readData(startoffset, buffersize);
if (sup_packet[startoffset] != 0x53 || sup_packet[startoffset + 1] != 0x50)
{
unreadData(1, buffersize - 1);
return b;
}
startoffset += 2;
return !b;
}
/**
*
*/
private boolean readPicture()
{
boolean b = false;
int read = 0;
int pts_field = 8;
int length_field = 2;
pts_value = CommonParsing.readPTS(sup_packet, startoffset, pts_field, CommonParsing.BYTEREORDERING, false);
startoffset += pts_field;
packetlength = CommonParsing.getIntValue(sup_packet, startoffset, length_field, !CommonParsing.BYTEREORDERING);
read = readData(buffersize, packetlength - length_field);
if (read < packetlength - length_field)
{
Common.setMessage("!> packet too short");
return b;
}
return !b;
}
/**
*
*/
private void writePicture()
{
int offset = 0;
writePictureIndex();
offset = writeFirstPack(offset);
while (offset > 0)
offset = writeSubPack(offset);
}
/**
* many nice confusing additions :)
*/
private int writeFirstPack(int offset)
{
if (offset >= packetlength)
return -1;
int max_size = packsize - pack_header.length - pes_header.length - pes_extension1.length - 1;
int length = packetlength - offset >= max_size ? max_size : packetlength - offset;
int padding = max_size - length;
int headerlength = pes_extension1.length + 1;
System.arraycopy(pes_extension1, 0, export_pack, pack_header.length + pes_header.length, pes_extension1.length);
if (padding <= padding_header.length)
{
headerlength += padding;
Arrays.fill(export_pack, pack_header.length + pes_header.length + pes_extension1.length, pack_header.length + pes_header.length + pes_extension1.length + padding, (byte) 0xFF);
CommonParsing.setPES_LengthField(export_pack, pack_header.length, headerlength + length);
CommonParsing.setValue(export_pack, pack_header.length + pes_header.length + 2, 1, !CommonParsing.BYTEREORDERING, 5 + padding);
CommonParsing.setPES_PTSField(export_pack, pack_header.length, pts_value);
CommonParsing.setPES_SubIdField(export_pack, pack_header.length, pes_header.length, pes_extension1.length + padding, stream_id);
}
else
{
System.arraycopy(padding_header, 0, export_pack, pack_header.length + pes_header.length + headerlength + length, padding_header.length);
CommonParsing.setPES_LengthField(export_pack, pack_header.length + pes_header.length + headerlength + length, padding - padding_header.length);
Arrays.fill(export_pack, pack_header.length + pes_header.length + headerlength + length + padding_header.length, packsize, (byte) 0xFF);
CommonParsing.setPES_LengthField(export_pack, pack_header.length, headerlength + length);
CommonParsing.setPES_PTSField(export_pack, pack_header.length, pts_value);
CommonParsing.setPES_SubIdField(export_pack, pack_header.length, pes_header.length, pes_extension1.length, stream_id);
}
System.arraycopy(sup_packet, startoffset + offset, export_pack, pack_header.length + pes_header.length + headerlength, length);
writeData();
offset += length;
return offset;
}
/**
*
*/
private int writeSubPack(int offset)
{
if (offset >= packetlength)
return -1;
int max_size = packsize - pack_header.length - pes_header.length - pes_extension2.length - 1;
int length = packetlength - offset >= max_size ? max_size : packetlength - offset;
int padding = max_size - length;
int headerlength = pes_extension2.length + 1;
System.arraycopy(pes_extension2, 0, export_pack, pack_header.length + pes_header.length, pes_extension2.length);
if (padding <= padding_header.length)
{
headerlength += padding;
Arrays.fill(export_pack, pack_header.length + pes_header.length + pes_extension2.length, pack_header.length + pes_header.length + pes_extension2.length + padding, (byte) 0xFF);
CommonParsing.setPES_LengthField(export_pack, pack_header.length, headerlength + length);
CommonParsing.setValue(export_pack, pack_header.length + pes_header.length + 2, 1, !CommonParsing.BYTEREORDERING, padding);
CommonParsing.setPES_SubIdField(export_pack, pack_header.length, pes_header.length, pes_extension2.length + padding, stream_id);
}
else
{
System.arraycopy(padding_header, 0, export_pack, pack_header.length + pes_header.length + headerlength + length, padding_header.length);
CommonParsing.setPES_LengthField(export_pack, pack_header.length + pes_header.length + headerlength + length, padding - padding_header.length);
Arrays.fill(export_pack, pack_header.length + pes_header.length + headerlength + length + padding_header.length, packsize, (byte) 0xFF);
CommonParsing.setPES_LengthField(export_pack, pack_header.length, length + headerlength);
CommonParsing.setPES_SubIdField(export_pack, pack_header.length, pes_header.length, pes_extension2.length, stream_id);
}
System.arraycopy(sup_packet, startoffset + offset, export_pack, pack_header.length + pes_header.length + headerlength, length);
writeData();
offset += length;
return offset;
}
/**
*
*/
private boolean initStreams()
{
boolean b = false;
try {
outputfile_sub = xInputFile + ".sub";
outputfile_idx = xInputFile + ".idx";
inputstream = new PushbackInputStream(xInputFile.getInputStream(), buffersize);
outputstream = new BufferedOutputStream(new FileOutputStream(outputfile_sub), 1024000);
printstream = new PrintWriter(new FileOutputStream(outputfile_idx));
Common.setMessage("");
Common.setMessage("-> create VobSub Files (idx + sub) : " + outputfile_sub);
return !b;
} catch (IOException e) {
Common.setExceptionMessage(e);
}
return b;
}
/**
*
*/
private void closeStreams()
{
try {
inputstream.close();
outputstream.flush();
outputstream.close();
printstream.flush();
printstream.close();
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
*
*/
private int readData(int offset, int length)
{
int read = 0;
try {
read = inputstream.read(sup_packet, offset, length);
read_position += read;
} catch (IOException e) {
Common.setExceptionMessage(e);
}
return read;
}
/**
*
*/
private int unreadData(int offset, int length)
{
try {
inputstream.unread(sup_packet, offset, length);
read_position -= length;
return length;
} catch (IOException e) {
Common.setExceptionMessage(e);
}
return 0;
}
/**
*
*/
private void writeData()
{
try {
outputstream.write(export_pack);
write_position += packsize;
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
*
*/
private void writePictureIndex()
{
printstream.print("timestamp: " + Common.formatTime_2a(pts_value / 90) + ", ");
printstream.println("filepos: " + Common.adaptString(Long.toHexString(write_position), 9));
}
/**
*
*/
private void initIndex(Object[] colour_table)
{
printstream.println("# VobSub index file, v7 (do not modify this line!)");
printstream.println("size: 720x576");
printstream.println("org: 0, 0");
printstream.println("scale: 100%, 100%");
printstream.println("alpha: 100%");
printstream.println("smooth: OFF");
printstream.println("fadein/out: 0, 0");
printstream.println("align: OFF at LEFT TOP");
printstream.println("time offset: 0");
printstream.println("forced subs: OFF");
printColourTable(colour_table);
printstream.println("custom colors: OFF, tridx: 1000, colors: 600000, 101010, ffffff, a9a9a9");
printstream.println("langidx: 0");
printstream.println("id: --, index: 0");
printstream.println("");
}
/**
*
*/
private void printColourTable(Object[] colour_table)
{
int max_indices = 16;
printstream.print("palette: ");
String[] std_colour_table = {
"600000", "101010" , "ffffff", "a9a9a9", "4d4d4d", "d7d7d7", "d7d7d7", "d7d7d7",
"d7d7d7", "d7d7d7", "d7d7d7", "d7d7d7", "d7d7d7", "d7d7d7", "d7d7d7", "d7d7d7"
};
if (colour_table == null)
colour_table = new Object[0];
else
{
for (int i = 0, j = colour_table.length; i < j && i < max_indices; i++)
{
printstream.print(String.valueOf(Common.adaptString(Integer.toHexString(0xFFFFFF & Integer.parseInt(colour_table[i].toString())), 6)));
if (i < max_indices - 1)
printstream.print(", ");
}
for (int i = colour_table.length, j = std_colour_table.length; i < j && i < max_indices; i++)
{
printstream.print(String.valueOf(Common.adaptString(std_colour_table[i], 6)));
if (i < max_indices - 1)
printstream.print(", ");
}
}
printstream.println();
}
}