peertube-live-streaming/encoder/src/main/java/com/pedro/encoder/input/decoder/BaseDecoder.java

217 lines
6.1 KiB
Java

/*
* Copyright (C) 2021 pedroSG94.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pedro.encoder.input.decoder;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;
import androidx.annotation.RequiresApi;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Map;
public abstract class BaseDecoder {
protected static String TAG = "BaseDecoder";
protected LoopFileInterface loopFileInterface;
protected MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
protected MediaExtractor extractor;
protected MediaCodec codec;
protected volatile boolean running = false;
protected MediaFormat mediaFormat;
private HandlerThread handlerThread;
protected String mime = "";
protected boolean loopMode = false;
protected volatile long seekTime = 0;
protected volatile long startMs = 0;
protected long duration;
public BaseDecoder(LoopFileInterface loopFileInterface) {
this.loopFileInterface = loopFileInterface;
}
public boolean initExtractor(String filePath) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(filePath);
return extract(extractor);
}
public boolean initExtractor(FileDescriptor fileDescriptor) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(fileDescriptor);
return extract(extractor);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean initExtractor(AssetFileDescriptor assetFileDescriptor) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(assetFileDescriptor);
return extract(extractor);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public boolean initExtractor(MediaDataSource mediaDataSource) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(mediaDataSource);
return extract(extractor);
}
public boolean initExtractor(String filePath, Map<String, String> headers) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(filePath, headers);
return extract(extractor);
}
public boolean initExtractor(FileDescriptor fileDescriptor, long offset, long length)
throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(fileDescriptor, offset, length);
return extract(extractor);
}
public boolean initExtractor(Context context, Uri uri, Map<String, String> headers)
throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(context, uri, headers);
return extract(extractor);
}
public void start() {
Log.i(TAG, "start decoder");
running = true;
handlerThread = new HandlerThread(TAG);
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
codec.start();
handler.post(new Runnable() {
@Override
public void run() {
try {
decode();
} catch (IllegalStateException e) {
Log.i(TAG, "Decoding error", e);
} catch (NullPointerException e) {
Log.i(TAG, "Decoder maybe was stopped");
Log.i(TAG, "Decoding error", e);
}
}
});
}
public void stop() {
Log.i(TAG, "stop decoder");
running = false;
stopDecoder();
if (extractor != null) {
extractor.release();
extractor = null;
mime = "";
}
}
protected boolean prepare(Surface surface) {
try {
codec = MediaCodec.createDecoderByType(mime);
codec.configure(mediaFormat, surface, null, 0);
return true;
} catch (IOException e) {
Log.e(TAG, "Prepare decoder error:", e);
return false;
}
}
protected void resetCodec(Surface surface) {
boolean wasRunning = running;
stopDecoder();
if (extractor != null) seekTime = extractor.getSampleTime() / 1000;
prepare(surface);
if (wasRunning) {
start();
}
}
protected void stopDecoder() {
running = false;
seekTime = 0;
if (handlerThread != null) {
if (handlerThread.getLooper() != null) {
if (handlerThread.getLooper().getThread() != null) {
handlerThread.getLooper().getThread().interrupt();
}
handlerThread.getLooper().quit();
}
handlerThread.quit();
if (codec != null) {
try {
codec.flush();
} catch (IllegalStateException ignored) { }
}
//wait for thread to die for 500ms.
try {
handlerThread.getLooper().getThread().join(500);
} catch (Exception ignored) { }
handlerThread = null;
}
try {
codec.stop();
codec.release();
codec = null;
} catch (IllegalStateException | NullPointerException e) {
codec = null;
}
}
public void moveTo(double time) {
extractor.seekTo((long) (time * 10E5), MediaExtractor.SEEK_TO_CLOSEST_SYNC);
seekTime = extractor.getSampleTime() / 1000;
startMs = System.currentTimeMillis();
}
public void setLoopMode(boolean loopMode) {
this.loopMode = loopMode;
}
public boolean isLoopMode() {
return loopMode;
}
public double getDuration() {
return duration / 10E5;
}
public double getTime() {
if (running) {
return extractor.getSampleTime() / 10E5;
} else {
return 0;
}
}
protected abstract boolean extract(MediaExtractor extractor);
protected abstract void decode();
}