/*
* @(#)Preview.java - prepare files for previewing
*
* Copyright (c) 2004-2009 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.video;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.sourceforge.dvb.projectx.xinput.XInputFile;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.parser.CommonParsing;
public class Preview extends Object {
private byte preview_data[];
private int loadSizeForward;
private int active_collection;
private int processed_PID;
private int position[];
private String processed_file;
private boolean silent = true;
private List positionList;
private PreviewObject preview_object;
private Object[] predefined_Pids;
public Preview(int _loadSizeForward)
{
loadSizeForward = _loadSizeForward;
position = new int[2];
positionList = new ArrayList();
processed_PID = -1;
processed_file = "";
}
/**
*
*/
public String getProcessedFile()
{
return processed_file;
}
/**
*
*/
public String getProcessedPID()
{
if (processed_PID < 0)
return "no (P)ID";
return ("(P)ID 0x" + Common.adaptString(Integer.toHexString(processed_PID).toUpperCase(), 4));
}
/**
*
*/
public long silentload(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection)
{
return load(startposition, size, previewList, direction, all_gops, fast_decode, y_gain, _predefined_Pids, _active_collection, true);
}
/**
*
*/
public long load(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection)
{
return load(startposition, size, previewList, direction, all_gops, fast_decode, y_gain, _predefined_Pids, _active_collection, false);
}
/**
*
*/
public long load(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection, boolean silent)
{
predefined_Pids = _predefined_Pids;
active_collection = _active_collection;
preview_data = new byte[size];
int filetype = 0;
int subfiletype = 0;
int read_offset = 0;
try {
for (int i = 0; i < previewList.size(); i++)
{
preview_object = (PreviewObject) previewList.get(i);
filetype = preview_object.getType();
if (startposition < preview_object.getEnd())
{
XInputFile lXInputFile = (XInputFile) preview_object.getFile();
lXInputFile.randomAccessOpen("r");
lXInputFile.randomAccessSeek(startposition - preview_object.getStart());
lXInputFile.randomAccessRead(preview_data, read_offset, size);
lXInputFile.randomAccessClose();
if (preview_object.getEnd() - startposition < size && i < previewList.size() - 1)
{
i++;
int diff = (int)(preview_object.getEnd() - startposition);
byte data2[] = new byte[size];
preview_object = (PreviewObject) previewList.get(i);
lXInputFile = (XInputFile) preview_object.getFile();
lXInputFile.randomAccessSingleRead(data2, 0);
System.arraycopy(data2, 0, preview_data, diff, size - diff);
data2 = null;
}
subfiletype = lXInputFile.getStreamInfo().getStreamSubType();
break;
}
}
preview_data = search(preview_data, startposition, filetype, subfiletype);
long newposition = Common.getMpvDecoderClass().decodeArray(preview_data, direction, all_gops, fast_decode, y_gain, silent);
for (int i = positionList.size() - 1; i >= 0; i--)
{
position = (int[]) positionList.get(i);
if (position[1] <= newposition)
{
startposition += position[0];
i = 0;
}
}
if (positionList.size() == 0)
startposition += newposition;
for (int i = 0; !silent && i < previewList.size(); i++)
{
preview_object = (PreviewObject)previewList.get(i);
if (startposition < preview_object.getEnd())
{
processed_file = "" + i + "/" + (previewList.size() - 1) + " - " + preview_object.getFile().getName();
break;
}
}
preview_data = null;
if (!silent)
{
Common.getMpvDecoderClass().setProcessedPosition(startposition, previewList);
Common.getMpvDecoderClass().setPidAndFileInfo(getProcessedPID() + " Part: " + getProcessedFile());
Common.getGuiInterface().repaintPicturePanel();
}
} catch (Exception e) {
Common.setExceptionMessage(e);
}
return startposition;
}
/**
*
*/
public long previewFile(XInputFile lXInputFile, long startposition, int size, boolean all_gops, boolean fast_decode, int y_gain)
{
preview_data = new byte[size];
predefined_Pids = new Object[0];
int filetype = lXInputFile.getStreamInfo().getStreamType();
int subfiletype = lXInputFile.getStreamInfo().getStreamSubType();
try {
lXInputFile.randomAccessOpen("r");
lXInputFile.randomAccessSeek(startposition);
lXInputFile.randomAccessRead(preview_data, 0, size);
lXInputFile.randomAccessClose();
} catch (IOException e) {
Common.setExceptionMessage(e);
}
preview_data = search(preview_data, startposition, filetype, subfiletype);
long newposition = Common.getMpvDecoderClass().decodeArray(preview_data, false, all_gops, fast_decode, y_gain, true);
startposition += newposition;
preview_data = null;
//System.gc();
return startposition;
}
/**
*
*/
private byte[] search(byte data[], long startposition, int filetype, int subfiletype)
{
positionList.clear();
int[] include = new int[predefined_Pids.length];
for (int i = 0; i < include.length; i++)
include[i] = Integer.parseInt(predefined_Pids[i].toString().substring(2), 16);
Arrays.sort(include);
//do the parse
switch (filetype)
{
case CommonParsing.ES_MPV_TYPE:
return data;
case CommonParsing.MPEG2PS_TYPE:
case CommonParsing.PES_AV_TYPE:
return parseMPG2(data, include);
case CommonParsing.MPEG1PS_TYPE:
return parseMPG1(data, include);
case CommonParsing.PVA_TYPE:
return parsePVA(data, include);
case CommonParsing.TS_TYPE:
if (subfiletype == CommonParsing.TS_TYPE_192BYTE>>>8)
return parseTS192(data, include);
else
return parseTS(data, include);
}
return data;
}
/**
* TS 188
*/
private byte[] parseTS(byte[] data, int[] include)
{
ByteArrayOutputStream array = new ByteArrayOutputStream();
byte[] hav_chunk = { 0x5B, 0x48, 0x4F, 0x4A, 0x49, 0x4E, 0x20, 0x41 }; //'[HOJIN A'
boolean save = false;
int mark = 0;
int offset = 0;
int ID = -1;
int a = 0;
for (int PID, dl = data.length - 9; a < dl; a++)
{
//humax chunk
if (data[a] == 0x7F && data[a + 1] == 0x41 && data[a + 2] == 4 && data[a + 3] == (byte)0xFD)
{
if (save && mark <= a)
array.write(data, mark, a - mark);
save = false;
a += 1183;
mark = a + 1;
continue;
}
//koscom chunk
if (a < data.length - 5 && data[a + 2] == 0 && data[a + 3] == 0 && data[a + 4] == 0x47)
{
if (save && mark <= a)
array.write(data, mark, a - mark);
save = false;
a += 3;
mark = a + 1;
continue;
}
//jepssen chunk
if (a < data.length - 36 && data[a + 2] == 0 && data[a + 3] == 0 && data[a + 36] == 0x47)
{
if (save && mark <= a)
array.write(data, mark, a - mark);
save = false;
a += 35;
mark = a + 1;
continue;
}
//ts:
// if (a < data.length - 188 && data[a] == 0x47)
if (a < data.length && data[a] == 0x47)
{
int chunk_offset = 0;
//handan chunk
// if (data[a + 188] != 0x47 && data[a + 188] != 0x7F)
if (a < data.length - 188 && data[a + 188] != 0x47 && data[a + 188] != 0x7F)
{
int i = a + 188;
int j;
int k = a + 189;
int l = hav_chunk.length;
while (i > a)
{
j = 0;
while (i > a && data[i] != hav_chunk[j])
i--;
for ( ; i > a && j < l && i + j < k; j++)
if (data[i + j] != hav_chunk[j])
break;
/**
* found at least one byte of chunk
*/
if (j > 0)
{
/** ident of chunk doesnt match completely */
if (j < l && i + j < k)
{
i--;
continue;
}
/**
* re-sorts packet in array
*/
if (i + 0x200 + (k - i) < data.length)
{
chunk_offset = 0x200;
System.arraycopy(data, i + chunk_offset, data, i, k - i - 1);
System.arraycopy(data, a, data, a + chunk_offset, i - a);
}
break;
}
}
//jepssen chunk
if (chunk_offset == 0 && a < data.length - 224 && data[a + 190] == 0 && data[a + 191] == 0 && data[a + 224] == 0x47)
{}
//koscom chunk
else if (chunk_offset == 0 && a < data.length - 224 && data[a + 190] == 0 && data[a + 191] == 0 && data[a + 192] == 0x47)
{}
else if (chunk_offset == 0)
continue;
}
if (save && mark <= a)
array.write(data, mark, a - mark);
mark = a;
PID = (0x1F & data[a + 1])<<8 | (0xFF & data[a + 2]);
if (include.length > 0 && Arrays.binarySearch(include, PID) < 0)
save = false;
else if ((ID == PID || ID == -1) && (0xD & data[a + 3]>>>4) == 1)
{
if ((0x20 & data[a + 3]) != 0) //payload start position, adaption field
mark = a + 5 + (0xFF & data[a + 4]);
else
mark = a + 4;
if ((0x40 & data[a + 1]) != 0) //start indicator
{
if (data[mark] == 0 && data[mark + 1] == 0 && data[mark + 2] == 1 && (0xF0 & data[mark + 3]) == 0xE0) //DM06032004 081.6 int18 fix
{
ID = PID;
mark = mark + 9 + (0xFF & data[mark + 8]);
int[] currentposition = { a, array.size() };
positionList.add(currentposition);
}
else
save = false;
}
if (ID == PID)
save = true;
}
else
save = false;
a += 187;
a += chunk_offset;
mark += chunk_offset;
}
}
//save last marked packet
if (save && mark < data.length && a <= data.length)
array.write(data, mark, a - mark);
processed_PID = ID;
return array.toByteArray();
}
/**
* TS 192
*/
private byte[] parseTS192(byte[] data, int[] include)
{
ByteArrayOutputStream array = new ByteArrayOutputStream();
boolean save = false;
int mark = 0;
int offset = 0;
int ID = -1;
for (int a = 0, PID, dl = data.length - 9; a < dl; a++)
{
//ts:
if (a < data.length - 192 && data[a] == 0x47)
{
if (save && mark <= a)
array.write(data, mark, a - mark - 4);
mark = a;
PID = (0x1F & data[a + 1])<<8 | (0xFF & data[a + 2]);
if (include.length > 0 && Arrays.binarySearch(include, PID) < 0)
save = false;
else if ((ID == PID || ID == -1) && (0xD & data[a + 3]>>>4) == 1)
{
if ((0x20 & data[a + 3]) != 0) //payload start position, adaption field
mark = a + 5 + (0xFF & data[a + 4]);
else
mark = a + 4;
if ((0x40 & data[a + 1]) != 0) //start indicator
{
if (data[mark] == 0 && data[mark + 1] == 0 && data[mark + 2] == 1 && (0xF0 & data[mark + 3]) == 0xE0) //DM06032004 081.6 int18 fix
{
ID = PID;
mark = mark + 9 + (0xFF & data[mark + 8]);
int[] currentposition = { a, array.size() };
positionList.add(currentposition);
}
else
save = false;
}
if (ID == PID)
save = true;
}
else
save = false;
a += 191;
}
}
processed_PID = ID;
return array.toByteArray();
}
/**
* PES_AV + MPG2
*/
private byte[] parseMPG2(byte[] pes_data, int[] include)
{
ByteArrayOutputStream array = new ByteArrayOutputStream();
boolean savePacket = false;
int mark = 0;
int pes_headerlength = 9;
int pes_offset = 6;
int pes_payloadlength = 0;
int pes_ID = -1;
for (int offset = 0, offset2, pes_ID_tmp, returncode, j = pes_data.length - pes_headerlength; offset < j; )
{
if ((returncode = CommonParsing.validateStartcode(pes_data, offset)) < 0)
{
offset += -returncode;
continue;
}
pes_ID_tmp = CommonParsing.getPES_IdField(pes_data, offset);
if (pes_ID_tmp < CommonParsing.SYSTEM_END_CODE)
{
offset += 4;
continue;
}
if (savePacket)
array.write(pes_data, mark, offset - mark);
mark = offset;
if (include.length > 0 && Arrays.binarySearch(include, pes_ID_tmp) < 0)
savePacket = false;
else if ((pes_ID == pes_ID_tmp || pes_ID == -1) && (0xF0 & pes_ID_tmp) == 0xE0)
{
pes_ID = pes_ID_tmp;
mark = offset + pes_headerlength + CommonParsing.getPES_ExtensionLengthField(pes_data, offset);
int[] currentposition = { offset, array.size() };
positionList.add(currentposition);
savePacket = true;
}
else
savePacket = false;
offset2 = pes_ID_tmp < CommonParsing.SYSTEM_START_CODE ? 12 : 0; //BA
pes_payloadlength = CommonParsing.getPES_LengthField(pes_data, offset);
if (pes_payloadlength == 0) //zero length
offset2 = pes_headerlength;
offset += (offset2 == 0 ? (pes_offset + pes_payloadlength) : offset2);
}
processed_PID = pes_ID;
return array.toByteArray();
}
/**
* MPG1
*/
private byte[] parseMPG1(byte[] pes_data, int[] include)
{
ByteArrayOutputStream array = new ByteArrayOutputStream();
boolean savePacket = false;
int mark = 0;
int pes_headerlength = 7;
int pes_offset = 6;
int pes_payloadlength = 0;
int pes_ID = -1;
for (int offset = 0, offset2, pes_ID_tmp, returncode, shift, j = pes_data.length - pes_headerlength; offset < j; )
{
if ((returncode = CommonParsing.validateStartcode(pes_data, offset)) < 0)
{
offset += -returncode;
continue;
}
pes_ID_tmp = CommonParsing.getPES_IdField(pes_data, offset);
if (pes_ID_tmp < CommonParsing.SYSTEM_END_CODE)
{
offset += 4;
continue;
}
if (savePacket)
array.write(pes_data, mark, offset - mark);
mark = offset;
if (include.length > 0 && Arrays.binarySearch(include, pes_ID_tmp) < 0)
savePacket = false;
else if ((pes_ID == pes_ID_tmp || pes_ID == -1) && (0xF0 & pes_ID_tmp) == 0xE0)
{
pes_ID = pes_ID_tmp;
shift = offset + pes_offset;
skiploop:
while(true)
{
switch (0xC0 & pes_data[shift])
{
case 0x40:
shift += 2;
continue skiploop;
case 0x80:
shift += 3;
continue skiploop;
case 0xC0:
shift++;
continue skiploop;
case 0:
break;
}
switch (0x30 & pes_data[shift])
{
case 0x20:
shift += 5;
break skiploop;
case 0x30:
shift += 10;
break skiploop;
case 0x10:
shift += 5;
break skiploop;
case 0:
shift++;
break skiploop;
}
}
mark = shift;
int[] currentposition = { offset, array.size() };
positionList.add(currentposition);
savePacket = true;
}
else
savePacket = false;
offset2 = pes_ID_tmp < CommonParsing.SYSTEM_START_CODE ? 12 : 0; //BA
pes_payloadlength = CommonParsing.getPES_LengthField(pes_data, offset);
if (pes_payloadlength == 0) //zero length
offset2 = pes_headerlength;
offset += (offset2 == 0 ? (pes_offset + pes_payloadlength) : offset2);
}
processed_PID = pes_ID;
return array.toByteArray();
}
/**
* PVA
*/
private byte[] parsePVA(byte[] pes_data, int[] include)
{
ByteArrayOutputStream array = new ByteArrayOutputStream();
boolean savePacket = false;
int mark = 0;
int pes_headerlength = 8;
int pes_offset = 8;
int pes_payloadlength = 0;
int pes_ID = -1;
for (int offset = 0, pes_ID_tmp, j = pes_data.length - pes_headerlength; offset < j; )
{
if ((0xFF & pes_data[offset]) != 0x41 || (0xFF & pes_data[offset + 1]) != 0x56 || (0xFF & pes_data[offset + 4]) != 0x55)
{
offset++;
continue;
}
if (savePacket)
array.write(pes_data, mark, offset - mark);
mark = offset;
pes_ID_tmp = 0xFF & pes_data[offset + 2];
if (pes_ID_tmp == 1)
{
pes_ID = 1;
mark = ((0x10 & pes_data[offset + 5]) != 0) ? offset + pes_offset + 4 : offset + pes_offset;
int[] currentposition = { offset, array.size() };
positionList.add(currentposition);
savePacket = true;
}
else
savePacket = false;
pes_payloadlength = (0xFF & pes_data[offset + 6])<<8 | (0xFF & pes_data[offset + 7]);
offset += (pes_offset + pes_payloadlength);
}
processed_PID = pes_ID;
return array.toByteArray();
}
}