188 lines
5.2 KiB
Java
188 lines
5.2 KiB
Java
package com.pedro.encoder.input.decoder;
|
|
|
|
import android.media.MediaCodec;
|
|
import android.media.MediaExtractor;
|
|
import android.media.MediaFormat;
|
|
import android.util.Log;
|
|
import android.view.Surface;
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
|
|
/**
|
|
* Created by pedro on 20/06/17.
|
|
*/
|
|
public class VideoDecoder {
|
|
|
|
private final String TAG = "VideoDecoder";
|
|
|
|
private VideoDecoderInterface videoDecoderInterface;
|
|
private LoopFileInterface loopFileInterface;
|
|
private MediaExtractor videoExtractor;
|
|
private MediaCodec videoDecoder;
|
|
private MediaCodec.BufferInfo videoInfo = new MediaCodec.BufferInfo();
|
|
private boolean decoding;
|
|
private Thread thread;
|
|
private MediaFormat videoFormat;
|
|
private String mime = "";
|
|
private int width;
|
|
private int height;
|
|
private long duration;
|
|
private static boolean loopMode = false;
|
|
private volatile long seekTime = 0;
|
|
private volatile long startMs = 0;
|
|
|
|
public VideoDecoder(VideoDecoderInterface videoDecoderInterface,
|
|
LoopFileInterface loopFileInterface) {
|
|
this.videoDecoderInterface = videoDecoderInterface;
|
|
this.loopFileInterface = loopFileInterface;
|
|
}
|
|
|
|
public boolean initExtractor(String filePath) throws IOException {
|
|
decoding = false;
|
|
videoExtractor = new MediaExtractor();
|
|
videoExtractor.setDataSource(filePath);
|
|
for (int i = 0; i < videoExtractor.getTrackCount() && !mime.startsWith("video/"); i++) {
|
|
videoFormat = videoExtractor.getTrackFormat(i);
|
|
mime = videoFormat.getString(MediaFormat.KEY_MIME);
|
|
if (mime.startsWith("video/")) {
|
|
videoExtractor.selectTrack(i);
|
|
} else {
|
|
videoFormat = null;
|
|
}
|
|
}
|
|
if (videoFormat != null) {
|
|
width = videoFormat.getInteger(MediaFormat.KEY_WIDTH);
|
|
height = videoFormat.getInteger(MediaFormat.KEY_HEIGHT);
|
|
duration = videoFormat.getLong(MediaFormat.KEY_DURATION);
|
|
return true;
|
|
//video decoder not supported
|
|
} else {
|
|
mime = "";
|
|
videoFormat = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean prepareVideo(Surface surface) {
|
|
try {
|
|
videoDecoder = MediaCodec.createDecoderByType(mime);
|
|
videoDecoder.configure(videoFormat, surface, null, 0);
|
|
return true;
|
|
} catch (IOException e) {
|
|
Log.e(TAG, "Prepare decoder error:", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void start() {
|
|
decoding = true;
|
|
videoDecoder.start();
|
|
thread = new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
decodeVideo();
|
|
} catch (IllegalStateException e) {
|
|
Log.i(TAG, "Decoding error", e);
|
|
}
|
|
}
|
|
});
|
|
thread.start();
|
|
}
|
|
|
|
public void stop() {
|
|
decoding = false;
|
|
seekTime = 0;
|
|
if (thread != null) {
|
|
thread.interrupt();
|
|
try {
|
|
thread.join(100);
|
|
} catch (InterruptedException e) {
|
|
thread.interrupt();
|
|
}
|
|
thread = null;
|
|
}
|
|
try {
|
|
videoDecoder.stop();
|
|
videoDecoder.release();
|
|
videoDecoder = null;
|
|
} catch (IllegalStateException | NullPointerException e) {
|
|
videoDecoder = null;
|
|
}
|
|
if (videoExtractor != null) {
|
|
videoExtractor.release();
|
|
videoExtractor = null;
|
|
}
|
|
}
|
|
|
|
private void decodeVideo() throws IllegalStateException {
|
|
ByteBuffer[] inputBuffers = videoDecoder.getInputBuffers();
|
|
startMs = System.currentTimeMillis();
|
|
while (decoding) {
|
|
int inIndex = videoDecoder.dequeueInputBuffer(10000);
|
|
if (inIndex >= 0) {
|
|
ByteBuffer buffer = inputBuffers[inIndex];
|
|
int sampleSize = videoExtractor.readSampleData(buffer, 0);
|
|
if (sampleSize < 0) {
|
|
videoDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
|
} else {
|
|
videoDecoder.queueInputBuffer(inIndex, 0, sampleSize, videoExtractor.getSampleTime(), 0);
|
|
videoExtractor.advance();
|
|
}
|
|
}
|
|
int outIndex = videoDecoder.dequeueOutputBuffer(videoInfo, 10000);
|
|
if (outIndex >= 0) {
|
|
while (videoExtractor.getSampleTime() / 1000
|
|
> System.currentTimeMillis() - startMs + seekTime) {
|
|
try {
|
|
Thread.sleep(10);
|
|
} catch (InterruptedException e) {
|
|
if (thread != null) thread.interrupt();
|
|
return;
|
|
}
|
|
}
|
|
videoDecoder.releaseOutputBuffer(outIndex, videoInfo.size != 0);
|
|
}
|
|
if ((videoInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
|
seekTime = 0;
|
|
Log.i(TAG, "end of file out");
|
|
if (loopMode) {
|
|
loopFileInterface.onReset(true);
|
|
} else {
|
|
videoDecoderInterface.onVideoDecoderFinished();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public double getTime() {
|
|
if (decoding) {
|
|
return videoExtractor.getSampleTime() / 10E5;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public void moveTo(double time) {
|
|
videoExtractor.seekTo((long) (time * 10E5), MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
|
seekTime = videoExtractor.getSampleTime() / 1000;
|
|
startMs = System.currentTimeMillis();
|
|
}
|
|
|
|
public void setLoopMode(boolean loopMode) {
|
|
this.loopMode = loopMode;
|
|
}
|
|
|
|
public int getWidth() {
|
|
return width;
|
|
}
|
|
|
|
public int getHeight() {
|
|
return height;
|
|
}
|
|
|
|
public double getDuration() {
|
|
return duration / 10E5;
|
|
}
|
|
}
|