Reduce frame timeout crashes.
This commit is contained in:
parent
19b2658414
commit
dcc147d994
10 changed files with 97 additions and 71 deletions
|
@ -22,7 +22,7 @@ import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
|||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||
import org.thoughtcrime.securesms.util.MediaUtil;
|
||||
import org.thoughtcrime.securesms.video.InMemoryTranscoder;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.BadVideoException;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.EncodingException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -114,7 +114,7 @@ final class MediaResizer {
|
|||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | MmsException | BadVideoException e) {
|
||||
} catch (IOException | MmsException | EncodingException e) {
|
||||
throw new UndeliverableMessageException("Failed to transcode", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.thoughtcrime.securesms.logging.Log;
|
|||
import org.thoughtcrime.securesms.mms.MediaStream;
|
||||
import org.thoughtcrime.securesms.transport.UndeliverableMessageException;
|
||||
import org.thoughtcrime.securesms.util.MemoryFileDescriptor;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.BadVideoException;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.EncodingException;
|
||||
import org.thoughtcrime.securesms.video.videoconverter.MediaConverter;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
@ -80,7 +80,7 @@ public final class InMemoryTranscoder implements Closeable {
|
|||
: OUTPUT_FORMAT;
|
||||
}
|
||||
|
||||
public @NonNull MediaStream transcode(@NonNull Progress progress) throws IOException, UndeliverableMessageException, BadVideoException {
|
||||
public @NonNull MediaStream transcode(@NonNull Progress progress) throws IOException, UndeliverableMessageException, EncodingException {
|
||||
if (memoryFile != null) throw new AssertionError("Not expecting to reuse transcoder");
|
||||
|
||||
float durationSec = duration / 1000f;
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package org.thoughtcrime.securesms.video.videoconverter;
|
||||
|
||||
public final class BadVideoException extends Exception {
|
||||
BadVideoException() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package org.thoughtcrime.securesms.video.videoconverter;
|
||||
|
||||
public final class EncodingException extends Exception {
|
||||
EncodingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
EncodingException(String message, Exception inner) {
|
||||
super(message, inner);
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ final class InputSurface {
|
|||
/**
|
||||
* Creates an InputSurface from a Surface.
|
||||
*/
|
||||
public InputSurface(Surface surface) {
|
||||
InputSurface(Surface surface) throws TranscodingException {
|
||||
if (surface == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
@ -64,15 +64,15 @@ final class InputSurface {
|
|||
/**
|
||||
* Prepares EGL. We want a GLES 2.0 context and a surface that supports recording.
|
||||
*/
|
||||
private void eglSetup() {
|
||||
private void eglSetup() throws TranscodingException {
|
||||
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
|
||||
throw new RuntimeException("unable to get EGL14 display");
|
||||
throw new TranscodingException("unable to get EGL14 display");
|
||||
}
|
||||
int[] version = new int[2];
|
||||
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
|
||||
mEGLDisplay = null;
|
||||
throw new RuntimeException("unable to initialize EGL14");
|
||||
throw new TranscodingException("unable to initialize EGL14");
|
||||
}
|
||||
|
||||
// Configure EGL for pbuffer and OpenGL ES 2.0. We want enough RGB bits
|
||||
|
@ -89,7 +89,7 @@ final class InputSurface {
|
|||
int[] numConfigs = new int[1];
|
||||
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
|
||||
numConfigs, 0)) {
|
||||
throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config");
|
||||
throw new TranscodingException("unable to find RGB888+recordable ES2 EGL config");
|
||||
}
|
||||
|
||||
// Configure context for OpenGL ES 2.0.
|
||||
|
@ -101,7 +101,7 @@ final class InputSurface {
|
|||
attrib_list, 0);
|
||||
checkEglError("eglCreateContext");
|
||||
if (mEGLContext == null) {
|
||||
throw new RuntimeException("null context");
|
||||
throw new TranscodingException("null context");
|
||||
}
|
||||
|
||||
// Create a window surface, and attach it to the Surface we received.
|
||||
|
@ -112,7 +112,7 @@ final class InputSurface {
|
|||
surfaceAttribs, 0);
|
||||
checkEglError("eglCreateWindowSurface");
|
||||
if (mEGLSurface == null) {
|
||||
throw new RuntimeException("surface was null");
|
||||
throw new TranscodingException("surface was null");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,16 +143,16 @@ final class InputSurface {
|
|||
/**
|
||||
* Makes our EGL context and surface current.
|
||||
*/
|
||||
public void makeCurrent() {
|
||||
void makeCurrent() throws TranscodingException {
|
||||
if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
|
||||
throw new RuntimeException("eglMakeCurrent failed");
|
||||
throw new TranscodingException("eglMakeCurrent failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls eglSwapBuffers. Use this to "publish" the current frame.
|
||||
*/
|
||||
public boolean swapBuffers() {
|
||||
boolean swapBuffers() {
|
||||
return EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);
|
||||
}
|
||||
|
||||
|
@ -166,14 +166,14 @@ final class InputSurface {
|
|||
/**
|
||||
* Sends the presentation time stamp to EGL. Time is expressed in nanoseconds.
|
||||
*/
|
||||
public void setPresentationTime(long nsecs) {
|
||||
void setPresentationTime(long nsecs) {
|
||||
EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for EGL errors.
|
||||
*/
|
||||
private void checkEglError(String msg) {
|
||||
private static void checkEglError(String msg) throws TranscodingException {
|
||||
boolean failed = false;
|
||||
int error;
|
||||
while ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
|
||||
|
@ -181,7 +181,7 @@ final class InputSurface {
|
|||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException("EGL error encountered (see log)");
|
||||
throw new TranscodingException("EGL error encountered (see log)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ public final class MediaConverter {
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
public void convert() throws BadVideoException, IOException {
|
||||
public void convert() throws EncodingException, IOException {
|
||||
// Exception that may be thrown during release.
|
||||
Exception exception = null;
|
||||
Muxer muxer = null;
|
||||
|
@ -151,8 +151,7 @@ public final class MediaConverter {
|
|||
audioTrackConverter = AudioTrackConverter.create(mInput, mTimeFrom, mTimeTo, mAudioBitrate);
|
||||
|
||||
if (videoTrackConverter == null && audioTrackConverter == null) {
|
||||
Log.e(TAG, "no video and audio tracks");
|
||||
throw new BadVideoException();
|
||||
throw new EncodingException("No video and audio tracks");
|
||||
}
|
||||
|
||||
muxer = mOutput.createMuxer();
|
||||
|
@ -162,7 +161,7 @@ public final class MediaConverter {
|
|||
audioTrackConverter,
|
||||
muxer);
|
||||
|
||||
} catch (BadVideoException | IOException e) {
|
||||
} catch (EncodingException | IOException e) {
|
||||
Log.e(TAG, "error converting", e);
|
||||
exception = e;
|
||||
throw e;
|
||||
|
@ -207,7 +206,7 @@ public final class MediaConverter {
|
|||
}
|
||||
}
|
||||
if (exception != null) {
|
||||
throw new RuntimeException(exception);
|
||||
throw new EncodingException("Transcode failed", exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +216,7 @@ public final class MediaConverter {
|
|||
private void doExtractDecodeEditEncodeMux(
|
||||
final @Nullable VideoTrackConverter videoTrackConverter,
|
||||
final @Nullable AudioTrackConverter audioTrackConverter,
|
||||
final @NonNull Muxer muxer) throws IOException {
|
||||
final @NonNull Muxer muxer) throws IOException, TranscodingException {
|
||||
|
||||
boolean muxing = false;
|
||||
int percentProcessed = 0;
|
||||
|
|
|
@ -69,7 +69,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
* EGL context and surface will be made current. Creates a Surface that can be passed
|
||||
* to MediaCodec.configure().
|
||||
*/
|
||||
public OutputSurface(int width, int height) {
|
||||
OutputSurface(int width, int height) throws TranscodingException {
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
* Creates an OutputSurface using the current EGL context. Creates a Surface that can be
|
||||
* passed to MediaCodec.configure().
|
||||
*/
|
||||
public OutputSurface() {
|
||||
OutputSurface() throws TranscodingException {
|
||||
setup();
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
* Creates instances of TextureRender and SurfaceTexture, and a Surface associated
|
||||
* with the SurfaceTexture.
|
||||
*/
|
||||
private void setup() {
|
||||
private void setup() throws TranscodingException {
|
||||
mTextureRender = new TextureRender();
|
||||
mTextureRender.surfaceCreated();
|
||||
|
||||
|
@ -122,11 +122,11 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
/**
|
||||
* Prepares EGL. We want a GLES 2.0 context and a surface that supports pbuffer.
|
||||
*/
|
||||
private void eglSetup(int width, int height) {
|
||||
private void eglSetup(int width, int height) throws TranscodingException {
|
||||
mEGL = (EGL10)EGLContext.getEGL();
|
||||
mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
|
||||
if (!mEGL.eglInitialize(mEGLDisplay, null)) {
|
||||
throw new RuntimeException("unable to initialize EGL10");
|
||||
throw new TranscodingException("unable to initialize EGL10");
|
||||
}
|
||||
|
||||
// Configure EGL for pbuffer and OpenGL ES 2.0. We want enough RGB bits
|
||||
|
@ -142,7 +142,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
EGLConfig[] configs = new EGLConfig[1];
|
||||
int[] numConfigs = new int[1];
|
||||
if (!mEGL.eglChooseConfig(mEGLDisplay, attribList, configs, 1, numConfigs)) {
|
||||
throw new RuntimeException("unable to find RGB888+pbuffer EGL config");
|
||||
throw new TranscodingException("unable to find RGB888+pbuffer EGL config");
|
||||
}
|
||||
|
||||
// Configure context for OpenGL ES 2.0.
|
||||
|
@ -154,7 +154,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
attrib_list);
|
||||
checkEglError("eglCreateContext");
|
||||
if (mEGLContext == null) {
|
||||
throw new RuntimeException("null context");
|
||||
throw new TranscodingException("null context");
|
||||
}
|
||||
|
||||
// Create a pbuffer surface. By using this for output, we can use glReadPixels
|
||||
|
@ -167,7 +167,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs);
|
||||
checkEglError("eglCreatePbufferSurface");
|
||||
if (mEGLSurface == null) {
|
||||
throw new RuntimeException("surface was null");
|
||||
throw new TranscodingException("surface was null");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,13 +206,13 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
/**
|
||||
* Makes our EGL context and surface current.
|
||||
*/
|
||||
public void makeCurrent() {
|
||||
private void makeCurrent() throws TranscodingException {
|
||||
if (mEGL == null) {
|
||||
throw new RuntimeException("not configured for makeCurrent");
|
||||
throw new TranscodingException("not configured for makeCurrent");
|
||||
}
|
||||
checkEglError("before makeCurrent");
|
||||
if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
|
||||
throw new RuntimeException("eglMakeCurrent failed");
|
||||
throw new TranscodingException("eglMakeCurrent failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
/**
|
||||
* Replaces the fragment shader.
|
||||
*/
|
||||
public void changeFragmentShader(String fragmentShader) {
|
||||
void changeFragmentShader(String fragmentShader) throws TranscodingException {
|
||||
mTextureRender.changeFragmentShader(fragmentShader);
|
||||
}
|
||||
|
||||
|
@ -235,36 +235,38 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
* the OutputSurface object, after the onFrameAvailable callback has signaled that new
|
||||
* data is available.
|
||||
*/
|
||||
public void awaitNewImage() {
|
||||
final int TIMEOUT_MS = 500;
|
||||
void awaitNewImage() throws TranscodingException {
|
||||
final int TIMEOUT_MS = 750;
|
||||
|
||||
synchronized (mFrameSyncObject) {
|
||||
final long expireTime = System.currentTimeMillis() + TIMEOUT_MS;
|
||||
|
||||
while (!mFrameAvailable) {
|
||||
try {
|
||||
// Wait for onFrameAvailable() to signal us. Use a timeout to avoid
|
||||
// stalling the test if it doesn't arrive.
|
||||
mFrameSyncObject.wait(TIMEOUT_MS);
|
||||
if (!mFrameAvailable) {
|
||||
// TODO: if "spurious wakeup", continue while loop
|
||||
throw new RuntimeException("Surface frame wait timed out");
|
||||
|
||||
if (!mFrameAvailable && System.currentTimeMillis() > expireTime) {
|
||||
throw new TranscodingException("Surface frame wait timed out");
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
// shouldn't happen
|
||||
throw new RuntimeException(ie);
|
||||
throw new TranscodingException(ie);
|
||||
}
|
||||
}
|
||||
mFrameAvailable = false;
|
||||
}
|
||||
|
||||
// Latch the data.
|
||||
mTextureRender.checkGlError("before updateTexImage");
|
||||
TextureRender.checkGlError("before updateTexImage");
|
||||
mSurfaceTexture.updateTexImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the data from SurfaceTexture onto the current EGL surface.
|
||||
*/
|
||||
public void drawImage() {
|
||||
void drawImage() throws TranscodingException {
|
||||
mTextureRender.drawFrame(mSurfaceTexture);
|
||||
}
|
||||
|
||||
|
@ -273,7 +275,11 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
if (VERBOSE) Log.d(TAG, "new frame available");
|
||||
synchronized (mFrameSyncObject) {
|
||||
if (mFrameAvailable) {
|
||||
throw new RuntimeException("mFrameAvailable already set, frame could be dropped");
|
||||
try {
|
||||
throw new TranscodingException("mFrameAvailable already set, frame could be dropped");
|
||||
} catch (TranscodingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
mFrameAvailable = true;
|
||||
mFrameSyncObject.notifyAll();
|
||||
|
@ -283,7 +289,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
/**
|
||||
* Checks for EGL errors.
|
||||
*/
|
||||
private void checkEglError(String msg) {
|
||||
private void checkEglError(String msg) throws TranscodingException {
|
||||
boolean failed = false;
|
||||
int error;
|
||||
while ((error = mEGL.eglGetError()) != EGL10.EGL_SUCCESS) {
|
||||
|
@ -291,7 +297,7 @@ final class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
|
|||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException("EGL error encountered (see log)");
|
||||
throw new TranscodingException("EGL error encountered (see log)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ final class TextureRender {
|
|||
private int maPositionHandle;
|
||||
private int maTextureHandle;
|
||||
|
||||
public TextureRender() {
|
||||
TextureRender() {
|
||||
mTriangleVertices = ByteBuffer.allocateDirect(
|
||||
mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
|
||||
.order(ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
|
@ -88,11 +88,11 @@ final class TextureRender {
|
|||
Matrix.setIdentityM(mSTMatrix, 0);
|
||||
}
|
||||
|
||||
public int getTextureId() {
|
||||
int getTextureId() {
|
||||
return mTextureID;
|
||||
}
|
||||
|
||||
public void drawFrame(SurfaceTexture st) {
|
||||
void drawFrame(SurfaceTexture st) throws TranscodingException {
|
||||
checkGlError("onDrawFrame start");
|
||||
st.getTransformMatrix(mSTMatrix);
|
||||
|
||||
|
@ -131,32 +131,32 @@ final class TextureRender {
|
|||
/**
|
||||
* Initializes GL state. Call this after the EGL surface has been created and made current.
|
||||
*/
|
||||
public void surfaceCreated() {
|
||||
void surfaceCreated() throws TranscodingException {
|
||||
mProgram = createProgram(VERTEX_SHADER, FRAGMENT_SHADER);
|
||||
if (mProgram == 0) {
|
||||
throw new RuntimeException("failed creating program");
|
||||
throw new TranscodingException("failed creating program");
|
||||
}
|
||||
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
|
||||
checkGlError("glGetAttribLocation aPosition");
|
||||
if (maPositionHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for aPosition");
|
||||
throw new TranscodingException("Could not get attrib location for aPosition");
|
||||
}
|
||||
maTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTextureCoord");
|
||||
checkGlError("glGetAttribLocation aTextureCoord");
|
||||
if (maTextureHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for aTextureCoord");
|
||||
throw new TranscodingException("Could not get attrib location for aTextureCoord");
|
||||
}
|
||||
|
||||
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
|
||||
checkGlError("glGetUniformLocation uMVPMatrix");
|
||||
if (muMVPMatrixHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for uMVPMatrix");
|
||||
throw new TranscodingException("Could not get attrib location for uMVPMatrix");
|
||||
}
|
||||
|
||||
muSTMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uSTMatrix");
|
||||
checkGlError("glGetUniformLocation uSTMatrix");
|
||||
if (muSTMatrixHandle == -1) {
|
||||
throw new RuntimeException("Could not get attrib location for uSTMatrix");
|
||||
throw new TranscodingException("Could not get attrib location for uSTMatrix");
|
||||
}
|
||||
|
||||
int[] textures = new int[1];
|
||||
|
@ -180,15 +180,15 @@ final class TextureRender {
|
|||
/**
|
||||
* Replaces the fragment shader.
|
||||
*/
|
||||
public void changeFragmentShader(String fragmentShader) {
|
||||
public void changeFragmentShader(String fragmentShader) throws TranscodingException {
|
||||
GLES20.glDeleteProgram(mProgram);
|
||||
mProgram = createProgram(VERTEX_SHADER, fragmentShader);
|
||||
if (mProgram == 0) {
|
||||
throw new RuntimeException("failed creating program");
|
||||
throw new TranscodingException("failed creating program");
|
||||
}
|
||||
}
|
||||
|
||||
private int loadShader(int shaderType, String source) {
|
||||
private static int loadShader(int shaderType, String source) throws TranscodingException {
|
||||
int shader = GLES20.glCreateShader(shaderType);
|
||||
checkGlError("glCreateShader type=" + shaderType);
|
||||
GLES20.glShaderSource(shader, source);
|
||||
|
@ -204,7 +204,7 @@ final class TextureRender {
|
|||
return shader;
|
||||
}
|
||||
|
||||
private int createProgram(String vertexSource, String fragmentSource) {
|
||||
private int createProgram(String vertexSource, String fragmentSource) throws TranscodingException {
|
||||
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
|
||||
if (vertexShader == 0) {
|
||||
return 0;
|
||||
|
@ -235,11 +235,15 @@ final class TextureRender {
|
|||
return program;
|
||||
}
|
||||
|
||||
public void checkGlError(String op) {
|
||||
static void checkGlError(String msg) throws TranscodingException {
|
||||
boolean failed = false;
|
||||
int error;
|
||||
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
|
||||
Log.e(TAG, op + ": glError " + error);
|
||||
throw new RuntimeException(op + ": glError " + error);
|
||||
Log.e(TAG, msg + ": GLES20 error: 0x" + Integer.toHexString(error));
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
throw new TranscodingException("GLES20 error encountered (see log)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.thoughtcrime.securesms.video.videoconverter;
|
||||
|
||||
final class TranscodingException extends Exception {
|
||||
|
||||
TranscodingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
TranscodingException(Throwable inner) {
|
||||
super(inner);
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ final class VideoTrackConverter {
|
|||
final long timeTo,
|
||||
final int videoResolution,
|
||||
final int videoBitrate,
|
||||
final @NonNull String videoCodec) throws IOException {
|
||||
final @NonNull String videoCodec) throws IOException, TranscodingException {
|
||||
|
||||
final MediaExtractor videoExtractor = input.createExtractor();
|
||||
final int videoInputTrack = getAndSelectVideoTrackIndex(videoExtractor);
|
||||
|
@ -85,7 +85,7 @@ final class VideoTrackConverter {
|
|||
final long timeTo,
|
||||
final int videoResolution,
|
||||
final int videoBitrate,
|
||||
final @NonNull String videoCodec) throws IOException {
|
||||
final @NonNull String videoCodec) throws IOException, TranscodingException {
|
||||
|
||||
mTimeFrom = timeFrom;
|
||||
mTimeTo = timeTo;
|
||||
|
@ -175,7 +175,7 @@ final class VideoTrackConverter {
|
|||
}
|
||||
}
|
||||
|
||||
void step() throws IOException {
|
||||
void step() throws IOException, TranscodingException {
|
||||
// Extract video from file and feed to decoder.
|
||||
// Do not extract video if we have determined the output format but we are not yet
|
||||
// ready to mux the frames.
|
||||
|
|
Loading…
Add table
Reference in a new issue