VideoFile.java

Index Score
xnap.io
XNap 2

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.

MetricDescription
/* * XNap * * A pure java file sharing client. * * See 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; 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 xnap.io; import xnap.util.*; import java.io.*; import java.util.*; /** * Class to extract a video's play length and resolution. * * The video parsing code for avi and mpg 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 RepositoryFile { // --- 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; protected transient RandomAccessFile raf; // --- Constructor(s) --- public VideoFile(File f) { super(f); } // --- Method(s) --- public boolean parse() { boolean videoInfoFound = false; Debug.log("VideoFile: parsing " + getName()); try { raf = new RandomAccessFile(this, "r"); videoInfoFound = lookForAVI(); if (!videoInfoFound) { videoInfoFound = lookForMPEG(); } if (!videoInfoFound) { videoInfoFound = lookForASF(); } } catch (IOException ie) { Debug.log("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; ASFCodecList codecList = null; raf.seek(0); Debug.log("looking for asf"); while (!haveMainHeader || !haveStreamHeader || !haveCodecListHeader) { GUID guid; long size; guid = new GUID(); size = readBigEndianLong() - 24; Debug.log("GUID " + guid); Debug.log("size " + size); if (size < 0) return false; if (guid.equals(ASF_HEADER_GUID)) { Debug.log("Found header, skipping 6 bytes"); raf.skipBytes(6); } else if (guid.equals(ASF_FILE_PROPERTIES_GUID)) { Debug.log("Found asf main header"); if (size < ASF_FILE_PROPERTIES_SIZE) { Debug.log("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)) { Debug.log("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)) { Debug.log("Found codec list header"); codecList = new ASFCodecList(size); haveCodecListHeader = true; } else { Debug.log("Unknown packet, skipping " + size); raf.skipBytes((int)size); } } if (haveMainHeader && haveStreamHeader && haveCodecListHeader) { length = (int)(mainHeader.play_time / 10000000L); Debug.log("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); } Debug.log(sb); Debug.log(sbcount); int count = (buffer[16] & 0xFF) | ((buffer[17] & 0xFF) << 8) | ((buffer[18] & 0xFF) << 16) | ((buffer[19] & 0xFF) << 32); Debug.log("count " + count); int index = 20; for (int i = 0; i < count; i++) { short ctype = getShort(buffer, index); index += 2; Debug.log("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); Debug.log(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.

MetricValueDescription