VideoFile.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
org.xnap.plugin.viewer.videoinfo |
![]() |
![]() |
XNap 3 |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*
* XNap - A P2P framework and client.
*
* See the file AUTHORS for copyright information.
*
* 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.
*
* 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 org.xnap.plugin.viewer.videoinfo;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.apache.log4j.Logger;
import org.xnap.util.Formatter;
/**
* Class to extract a video's play length and resolution.
*
* The video parsing code for avi and mpeg files is taken from the ziga
* project. See README for more information.
* The video parsing code for asf files is taken from the avifile project
* (http://avifile.sourceforge.net/).
*/
public class VideoFile extends File
{
// --- Constant(s) ---
protected static final int PACK_START_CODE = 0x000001BA;
protected static final int SEQ_START_CODE = 0x000001B3;
protected static final int GROUP_START_CODE = 0x000001B8;
protected static final int MAX_FORWARD_READ_LENGTH = 50000;
protected static final int MAX_BACKWARD_READ_LENGTH = 3000000;
protected static final int ASF_FILE_PROPERTIES_SIZE = 80;
protected static final int ASF_STREAM_PROPERTIES_SIZE = 1024;
protected static final int GUID_SIZE = 16;
protected final transient GUID ASF_HEADER_GUID =
new GUID(0x75b22630, 0x668e, 0x11cf,
new byte[] { (byte)0xa6, (byte)0xd9, (byte)0x00, (byte)0xaa,
(byte)0x00, (byte)0x62, (byte)0xce,
(byte)0x6c });
protected final transient GUID ASF_FILE_PROPERTIES_GUID =
new GUID(0x8cabdca1, 0xa947, 0x11cf,
new byte[] { (byte)0x8e, (byte)0xe4, (byte)0x00, (byte)0xc0,
(byte)0x0c, (byte)0x20, (byte)0x53,
(byte)0x65 });
protected final transient GUID ASF_STREAM_PROPERTIES_GUID =
new GUID(0xb7dc0791, 0xa9b7, 0x11cf,
new byte[] { (byte)0x8e, (byte)0xe6, (byte)0x00, (byte)0xc0,
(byte)0x0c, (byte)0x20, (byte)0x53,
(byte)0x65 });
protected final transient GUID ASF_AUDIO_MEDIA_GUID =
new GUID(0xf8699e40, 0x5b4d, 0x11cf,
new byte[] { (byte)0xa8, (byte)0xfd, (byte)0x00, (byte)0x80,
(byte)0x5f, (byte)0x5c, (byte)0x44,
(byte)0x2b });
protected final transient GUID ASF_CODEC_LIST_GUID =
new GUID(0x86d15240, 0x311d, 0x11d0,
new byte[] { (byte)0xa3, (byte)0xa4, (byte)0x00, (byte)0xa0,
(byte)0xc9, (byte)0x03, (byte)0x48,
(byte)0xf6 });
// --- Data Field(s) ---
private int height = -1;
private int length = -1;
private int width = -1;
private static Logger logger = Logger.getLogger(VideoFile.class);
private transient RandomAccessFile raf;
private transient ASFCodecList codecList;
// --- Constructor(s) ---
public VideoFile(File f)
{
super(f, "");
}
// --- Method(s) ---
public boolean parse()
{
boolean videoInfoFound = false;
logger.debug("VideoFile: parsing " + getName());
try {
raf = new RandomAccessFile(this, "r");
videoInfoFound = lookForAVI();
if (!videoInfoFound) {
videoInfoFound = lookForMPEG();
}
if (!videoInfoFound) {
videoInfoFound = lookForASF();
}
}
catch (IOException ie) {
logger.debug("VideoFile " + ie);
}
finally {
try {
raf.close();
}
catch (IOException ie) {
}
}
return videoInfoFound;
}
public int getHeight()
{
return height;
}
public String getInfo()
{
return Formatter.formatLength(getLength());
}
public int getLength()
{
return length;
}
public int getWidth()
{
return width;
}
protected boolean lookForAVI() throws IOException
{
boolean avihFound = false;
raf.seek(0);
String s = readChunk();
if (s.equals("RIFF")) {
raf.skipBytes(4);
s = readChunk();
if (s.equals("AVI ")) {
while (true) {
s = readChunk();
raf.skipBytes(4);
if (s.equals("LIST")) {
s = readChunk();
if (s.equals("hdrl")) {
s = readChunk();
int avihLength = readBigEndianInt();
if (s.equals("avih") && avihLength >= 56) {
double microSecPerFrame = readBigEndianInt();
raf.skipBytes(12);
double totalFrames = readBigEndianInt();
// set member variable length
length = (int) (totalFrames * (microSecPerFrame / 1000000L));
raf.skipBytes(12);
// set members
width = readBigEndianInt();
height = readBigEndianInt();
raf.skipBytes(avihLength - 40);
avihFound = true;
}
}
}
else {
break;
}
}
}
}
return avihFound;
}
protected boolean lookForASF() throws IOException
{
boolean haveMainHeader = false;
boolean haveStreamHeader = false;
boolean haveCodecListHeader = false;
ASFMainHeader mainHeader = null;
ASFStreamHeader streamHeader = null;
raf.seek(0);
logger.debug("looking for asf");
while (!haveMainHeader || !haveStreamHeader || !haveCodecListHeader) {
GUID guid;
long size;
guid = new GUID();
size = readBigEndianLong() - 24;
logger.debug("GUID " + guid);
logger.debug("size " + size);
if (size < 0)
return false;
if (guid.equals(ASF_HEADER_GUID)) {
logger.debug("Found header, skipping 6 bytes");
raf.skipBytes(6);
}
else if (guid.equals(ASF_FILE_PROPERTIES_GUID)) {
logger.debug("Found asf main header");
if (size < ASF_FILE_PROPERTIES_SIZE) {
logger.debug("main header too small");
return false;
}
mainHeader = new ASFMainHeader();
raf.skipBytes((int) (size - ASF_FILE_PROPERTIES_SIZE));
haveMainHeader = true;
}
else if (guid.equals(ASF_STREAM_PROPERTIES_GUID)) {
logger.debug("Found stream props");
streamHeader = new ASFStreamHeader(size);
if (size > streamHeader.count) {
raf.skipBytes((int) (size - streamHeader.count));
}
haveStreamHeader = true;
}
else if (guid.equals(ASF_CODEC_LIST_GUID)) {
logger.debug("Found codec list header");
codecList = new ASFCodecList(size);
haveCodecListHeader = true;
}
else {
logger.debug("Unknown packet, skipping " + size);
raf.skipBytes((int)size);
}
}
if (haveMainHeader && haveStreamHeader && haveCodecListHeader) {
length = (int)(mainHeader.play_time / 10000000L);
logger.debug("asf length " + length);
if (streamHeader.videoHeader) {
height = streamHeader.height;
width = streamHeader.width;
}
return true;
}
return false;
}
protected boolean lookForMPEG() throws IOException
{
raf.seek(0);
if (nextStartCode()) {
if (raf.readInt() == PACK_START_CODE) {
byte[] b = new byte[6];
Long initialSCR = null;
raf.readFully(b);
if ((b[0] & 0xF0) == 0x20) {
initialSCR = getMPEGSCR(b);
}
else if ((b[0] & 0xC0) == 0x40) {
initialSCR = getMPEG2SCR(b);
}
boolean seqStartCodeFound = false;
while (true) {
if (nextStartCode()) {
if(raf.readInt() == SEQ_START_CODE) {
seqStartCodeFound = true;
break;
}
}
else {
break;
}
}
if (seqStartCodeFound) {
b = new byte[3];
raf.readFully(b);
width = (((b[0] & 0xff) << 4) | (b[1] & 0xf0));
height = (((b[1] & 0x0f) << 8) | (b[2] & 0xff));
boolean groupStartCodeFound = false;
raf.seek(raf.length());
while (true) {
if (previousStartCode()) {
if(raf.readInt() == PACK_START_CODE) {
groupStartCodeFound = true;
break;
}
}
else {
break;
}
raf.seek(raf.getFilePointer() - 4);
}
if (groupStartCodeFound) {
b = new byte[6];
Long lastSCR = null;
raf.readFully(b);
if ((b[0] & 0xF0) == 0x20) {
lastSCR = getMPEGSCR(b);
}
else if ((b[0] & 0xC0) == 0x40) {
lastSCR = getMPEG2SCR(b);
}
if (initialSCR != null && lastSCR != null) {
length = (int) (lastSCR.longValue()
- initialSCR.longValue());
}
}
return true;
}
}
}
return false;
}
protected final String readChunk() throws IOException
{
byte[] b = new byte[4];
raf.readFully(b);
return new String(b);
}
protected final short readBigEndianShort() throws IOException
{
byte[] b = new byte[2];
raf.readFully(b);
return (short) ((b[0] & 0xff) | ((b[1] & 0xff) << 8));
}
protected final int readBigEndianInt() throws IOException
{
byte[] b = new byte[4];
raf.readFully(b);
return (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16)
| ((b[3] & 0xff) << 24);
}
protected final long readBigEndianLong() throws IOException
{
return (readBigEndianInt() | (readBigEndianInt() << 32));
}
protected Long getMPEGSCR(byte[] b)
{
long scr;
// int highbit = (b[0] >> 3) & 0x01;
long low4Bytes = (((b[0] & 0xff) >> 1) & 0x03) << 30
|(b[1] & 0xff) << 22 | ((b[2] & 0xff) >> 1) << 15 |
(b[3] & 0xff) << 7 | (b[4] & 0xff) >> 1;
scr = low4Bytes;
scr /= 90000;
return new Long(scr);
}
protected Long getMPEG2SCR(byte[] b)
{
long scr;
// int highbit = (b[0] & 0x20) >> 5;
long low4Bytes = ((b[0] & 0x18) >> 3) << 30 | (b[0] & 0x03) << 28
| (b[1] & 0xff) << 20 | ((b[2] & 0xF8) >> 1) << 15 |
(b[2] & 0x03) << 13 | (b[3] & 0xff) << 5 | (b[4] & 0xff) >> 3;
int sys_clock_extension=(b[4] & 0x3) << 7 | ((b[5] & 0xff) >> 1);
scr = low4Bytes;
if (sys_clock_extension == 0) {
scr /= 90000;
return new Long(scr);
}
else {
//???
return null;
}
}
protected boolean nextStartCode()
{
byte[] b = new byte[1024];
int available;
boolean startCodeFound = false;
try {
for (int i = 0; i < MAX_FORWARD_READ_LENGTH && !startCodeFound;
i += available) {
available = raf.read(b);
if (available > 0) {
i += available;
for (int offset = 0; offset < available-2; offset++) {
if (b[offset] == 0 && b[offset+1] == 0 && b[offset+2] == 1) {
raf.seek(raf.getFilePointer() - (available-offset));
startCodeFound = true;
break;
}
}
}
else {
break;
}
}
}
catch(IOException e) {
}
return startCodeFound;
}
protected boolean previousStartCode()
{
byte[] b = new byte[8024];
boolean startCodeFound = false;
try {
for (int i = 0; i < MAX_BACKWARD_READ_LENGTH && !startCodeFound;
i += b.length) {
long fp = raf.getFilePointer() - b.length;
if (fp < 0) {
if (fp <= b.length) {
break;
}
fp = 0;
}
raf.seek(fp);
raf.readFully(b);
for (int offset = b.length-1; offset > 1; offset--) {
if (b[offset-2] == 0 && b[offset-1] == 0
&& b[offset] == 1) {
raf.seek(raf.getFilePointer() - (b.length-offset) - 2);
startCodeFound = true;
break;
}
}
if (!startCodeFound) {
raf.seek(raf.getFilePointer() - b.length);
}
}
} catch(IOException e) {
}
return startCodeFound;
}
protected class GUID
{
public int f1;
public short f2;
public short f3;
public byte[] f4;
public GUID() throws IOException
{
f4 = new byte[8];
f1 = readBigEndianInt();
f2 = readBigEndianShort();
f3 = readBigEndianShort();
for (int i = 0; i < f4.length; i++) {
f4[i] = raf.readByte();
}
}
public GUID(int f1, int f2, int f3, byte[] f4)
{
this.f1 = f1;
this.f2 = (short) f2;
this.f3 = (short) f3;
this.f4 = f4;
}
public boolean equals(Object o)
{
if (! (o instanceof GUID)) {
return false;
}
GUID g = (GUID) o;
if (f1 != g.f1 || f2 != g.f2 || f3 != g.f3) {
return false;
}
for (int i = 0; i < f4.length; i++) {
if (f4[i] != g.f4[i])
return false;
}
return true;
}
public String toString()
{
return "f1 " + f1 + "\n"
+ "f2 " + f2 + "\n"
+ "f3 " + f3 + "\n";
}
}
protected class ASFMainHeader
{
public GUID guid;
long file_size; // in bytes
// invalid if broadcasting
long create_time; // time of creation, in 100-nanosecond units since 1.1.1601
// invalid if broadcasting
long pkts_count; // how many packets are there in the file
// invalid if broadcasting
public long play_time; // play time, in 100-nanosecond units
// invalid if broadcasting
long send_time; // time to send file, in 100-nanosecond units
// invalid if broadcasting (could be ignored)
int preroll; // timestamp of the first packet, in milliseconds
// if nonzero - substract from time
int ignore; // preroll is 64bit - but let's just ignore it
int flags; // 0x01 - broadcast
// 0x02 - seekable
// rest is reserved should be 0
int min_pktsize; // size of a data packet
// invalid if broadcasting
int max_pktsize; // shall be the same as for min_pktsize
// invalid if broadcasting
int max_bitrate; // bandwith of stream in bps
public ASFMainHeader() throws IOException
{
guid = new GUID();
file_size = readBigEndianLong();
create_time = readBigEndianLong();
pkts_count = readBigEndianLong();
play_time = readBigEndianLong();
send_time = readBigEndianLong();
preroll = readBigEndianInt();
ignore = readBigEndianInt();
flags = readBigEndianInt();
min_pktsize = readBigEndianInt();
max_pktsize = readBigEndianInt();
max_bitrate = readBigEndianInt();
}
}
protected class ASFStreamHeader
{
public GUID stream_guid; // type of media stream
public GUID error_guid; // data error correction used
public long time_offset; // presentation time (in 100-nanosecond unit)
public int stream_size; // size of type-specific data
public int error_size; // size of error correct data
public short stream; // number ( 1, 2 ... )
public int reserved; // usually the same in both streams:
// Eyes on me.asf: 0x62dffd4
// Alsou - Solo.asf: 0x10
// Baby one more time.asf: 0x10
// Cure_LastDayOfSummer.wma: 0x818f900c
// Windows Movie Maker Sample File.wmv: 0x3f
public int width; // witdth of encoded image
public int height; // height of encoded image
public byte flags; // shall be set to 2
public short data_size;
// BITMAPINFOHEADER bh;
public int count = 0;
public boolean videoHeader = false;
public ASFStreamHeader(long size) throws IOException
{
if (count < size - GUID_SIZE) {
stream_guid = new GUID();
count += GUID_SIZE;
}
if (count < size - GUID_SIZE) {
error_guid = new GUID();
count += GUID_SIZE;
}
if (count < size - 8) {
time_offset = readBigEndianLong();
count += 8;
}
if (count < size - 4) {
stream_size = readBigEndianInt();
count += 4;
}
if (count < size - 4) {
error_size = readBigEndianInt();
count += 4;
}
if (count < size - 2) {
stream = readBigEndianShort();
count += 2;
}
if (count < size - 4) {
reserved = readBigEndianInt();
count += 4;
}
if (!stream_guid.equals(ASF_AUDIO_MEDIA_GUID)) {
videoHeader = true;
if (count < size - 4) {
width = readBigEndianInt();
count += 4;
}
if (count < size - 4) {
height = readBigEndianInt();
count += 4;
}
if (count < size - 1) {
flags = raf.readByte();
count += 1;
}
if (count < size - 2) {
data_size = readBigEndianShort();
count += 2;
}
}
}
}
protected class ASFCodecList
{
public final String[] names = { "Codec Name", "Codec Description",
"Information" };
public ASFCodecList(long size) throws IOException
{
byte[] buffer = new byte[(int)size];
raf.readFully(buffer);
StringBuffer sb = new StringBuffer(buffer.length);
StringBuffer sbcount = new StringBuffer(buffer.length);
for (int i = 0; i < buffer.length; i++) {
sb.append((char) buffer[i]);
sbcount.append(i % 10);
}
logger.debug(sb);
logger.debug(sbcount);
int count = (buffer[16] & 0xFF) | ((buffer[17] & 0xFF) << 8)
| ((buffer[18] & 0xFF) << 16) | ((buffer[19] & 0xFF) << 32);
logger.debug("count " + count);
int index = 20;
for (int i = 0; i < count; i++) {
short ctype = getShort(buffer, index);
index += 2;
logger.debug("ASF reader Codec Type: " + ctype);
for (int j = 0; j < 3; j++) {
short csize = getShort(buffer, index);
index += 2;
if (j < 2) {
csize *= 2;
String s = getString(buffer, index, csize);
logger.debug(names[j] + " " + s);
}
index += csize;
}
}
}
public short getShort(byte[] buffer, int offset)
{
return (short) ((buffer[offset] & 0xFF)
| ((buffer[offset + 1] & 0xFF) << 8));
}
public String getString(byte[] buffer, int offset, int length)
{
StringBuffer sb = new StringBuffer(length);
for (int i = 0; i < length && buffer[i] != '\0'; i += 2) {
sb.append((char) buffer[i]);
}
return sb.toString();
}
}
}
The table below shows all metrics for VideoFile.java.




