package net.pms.util;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class MpegUtil {
public static int getDurationFromMpeg(File f) throws IOException {
try (RandomAccessFile raf = new RandomAccessFile(f, "r")) {
if (raf.length() >= 500000) {
Map<Integer, Integer> ptsStart = checkRange(raf, 0, 250000, false);
if (ptsStart != null) {
Map<Integer, Integer> ptsEnd = checkRange(raf, 0, 250000, true);
if (ptsEnd != null) {
Iterator<Integer> iterator = ptsStart.keySet().iterator();
while (iterator.hasNext()) {
Integer id = iterator.next();
if (ptsEnd.get(id) != null) {
int dur = ptsEnd.get(id)
- ptsStart.get(id);
dur /= 90000;
return dur;
}
}
}
}
}
}
return 0;
}
private static Map<Integer, Integer> checkRange(RandomAccessFile raf, long startingPos,
int range, boolean end) throws IOException {
Map<Integer, Integer> pts = new HashMap<>();
byte buffer[] = new byte[range];
if (end) // statringPos not applicable for end==true
{
raf.seek(raf.length() - range);
} else {
raf.seek(0 + startingPos);
}
raf.read(buffer, 0, buffer.length);
int ps = 0;
int start = 0;
for (int i = 0; i < 400; i++) {
if (buffer[i] == 71 && buffer[i + 188] == 71) { // T
// ok
ps = 188;
start = i;
break;
} else if (buffer[i] == 71 && buffer[i + 192] == 71) { // MT
// ok
ps = 192;
start = i;
break;
}
}
if (ps == 0) {
return null;
}
for (int i = start; i < buffer.length - ps; i += ps) {
Integer id = (((buffer[i + 1] + 256) % 256) - 64) * 256 + ((buffer[i + 2] + 256) % 256); // calc id
if (buffer[i + 7] == -32 && buffer[i + 6] == 1) {
int diff = i + 7 + 4; // 47 50 11 11 00 00 01 E0 00 00 84 C0
// check pts
if ((buffer[diff] & 128) == 128 && (buffer[diff + 2] & 32) == 32
&& (pts.get(id) == null || (pts.get(id) != null && end))) {
pts.put(id, getTS(buffer, diff + 3));
}
}
}
return pts;
}
private static int getTS(byte buffer[], int diff) {
return (((((buffer[diff + 0] & 0xff) << 8) + (buffer[diff + 1] & 0xff)) >> 1) << 15)
+ ((((buffer[diff + 2] & 0xff) << 8) + (buffer[diff + 3] & 0xff)) >> 1);
}
/**
* @deprecated Use {@link #getPositionForTimeInMpeg(File, int)} instead.
* gets position for specified time in MPEG stream (M2TS, TS)
* @param f - file to check
* @param timeS - time (in seconds) to find
* @return position in stream (in bytes).
* @throws IOException
*/
@Deprecated
public static long getPossitionForTimeInMpeg(File f, int timeS) throws IOException {
return getPositionForTimeInMpeg(f, timeS);
}
/**
* gets position for specified time in MPEG stream (M2TS, TS)
* @param f - file to check
* @param timeS - time (in seconds) to find
* @return position in stream (in bytes).
* @throws IOException
*/
public static long getPositionForTimeInMpeg(File f, int timeS) throws IOException {
RandomAccessFile raf = new RandomAccessFile(f, "r");
Map<Integer, Integer> ptsStart = checkRange(raf, 0, 250000, false);
long currentPos = 0;
if (ptsStart != null && !ptsStart.isEmpty()) {
long minRangePos = 0;
long maxRangePos = raf.length();
boolean nextPosition = true;
while (maxRangePos - minRangePos > 250000 && nextPosition) {
nextPosition = false;
currentPos = minRangePos + (maxRangePos - minRangePos) / 2;
Map<Integer, Integer> ptsEnd = checkRange(raf, currentPos, 250000, false);
if (ptsEnd != null) {
Iterator<Integer> iterator = ptsStart.keySet().iterator();
while (iterator.hasNext()) {
Integer id = iterator.next();
if (ptsEnd.get(id) != null) {
int time = (ptsEnd.get(id) - ptsStart.get(id)) / 90000;
if (time == timeS) // found it
{
return currentPos;
}
nextPosition = true;
if (time > timeS) {
maxRangePos = currentPos;
} else {
minRangePos = currentPos;
}
break;
}
}
} else {
return currentPos;
}
}
}
return currentPos;
}
}