deepmedia / Transcoder

🎞 Hardware-accelerated video transcoding using Android MediaCodec APIs. Supports cropping, concatenation, clipping, audio processing, video speed and much more.
https://opensource.deepmedia.io/transcoder
Apache License 2.0
783 stars 166 forks source link

TranscodeEngine: Unexpected error while transcoding. #146

Closed Noloxs closed 3 years ago

Noloxs commented 3 years ago

Hi. I encountered a issue related to the MediaMetadataRetriever on a particular video When I attempt to transcode the video, it throws an exception that seems to originate from the MediaMetadataRetriever while attempting to get the videos location metadata.

NB.: The video is not a natural recorded video but made by saving a power point as a video.

Transcoder version: 0.10.3 Device type: Samsung Galaxy S21 Ultra Android API level: Android 11 (30)

Exact steps to reproduce the issue Attempt to transcode the attached video using the configuration show below.

Expected behavior The video is transcoded, possibly with some missing metadata

Imprementation:

 DefaultVideoStrategy videoStrategy = DefaultVideoStrategy.atMost(minor, major)
                    .frameRate(30)
                    .bitRate(getBitrate(recipientSize))
                    .keyFrameInterval(99) //This is in seconds and so we only have first keyframe
                    .build();

            FallbackAudioStrategy strategy = FallbackAudioStrategy.builder()
                    .channels(2)
                    .fallBackBitRate(96000)
                    .fallBackSampleRate(44100)
                    .build();

            //issue with devices that gives an error from the rk player that the video is 4k if the video height is higher than the frame screen height (if we rotate then no problem)
            if (videoHeight > videoWidth && videoHeight > recipientSize.getHeight() && rotation == 0) {
                rotation = 90;
            }

            LogHelper.d(TAG, "Transcoding video");
            DataSource source = new FilePathDataSource(video.getMediaPath());
            DataSource clip = new ClipDataSource(source, video.getFromTimeMs() * 1000, video.getToTimeMs() * 1000);

            Transcoder
                    .into(transcodedTemp.getAbsolutePath())
                    .addDataSource(clip)
                    .setAudioTrackStrategy(video.isMuted() ? new RemoveTrackStrategy() : strategy)
                    .setVideoTrackStrategy(videoStrategy)
                    .setVideoRotation(rotation)
                    .setListenerHandler(callbackHandler)
                    .setListener(new TranscoderListener() {
                        public void onTranscodeProgress(double progress) {
                            //there's a bug in the Transcoder listener so it would sometimes return a progress above 1.0
                            progress = Math.min(1.0, progress);
                            listener.onProgress((float) progress);
                        }

                        public void onTranscodeCompleted(int successCode) {
                            transcodingSuccess = true;
                            LogHelper.v(TAG, "VIDEO TRANSCODE SUCCESS");
                            completeTranscodingLatch.countDown();
                        }

                        public void onTranscodeCanceled() {
                            transcodingSuccess = false;
                            LogHelper.e(TAG, "VIDEO TRANSCODE CANCEL");
                            completeTranscodingLatch.countDown();
                        }

                        public void onTranscodeFailed(@NonNull Throwable exception) {
                            transcodingSuccess = false;
                            LogHelper.e(TAG, "VIDEO TRANSCODE FAILURE");
                            LogHelper.logException(exception);
                            completeTranscodingLatch.countDown();
                        }
                    }).transcode();

Exception:

    java.lang.IllegalStateException: No retriever available
        at android.media.MediaMetadataRetriever.nativeExtractMetadata(Native Method)
        at android.media.MediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.java:414)
        at com.otaliastudios.transcoder.source.DefaultDataSource.getLocation(DefaultDataSource.java:253)
        at com.otaliastudios.transcoder.source.DataSourceWrapper.getLocation(DataSourceWrapper.java:38)
        at com.otaliastudios.transcoder.source.DataSourceWrapper.getLocation(DataSourceWrapper.java:38)
        at com.otaliastudios.transcoder.internal.transcode.DefaultTranscodeEngine$location$1.invoke(DefaultTranscodeEngine.kt:54)
        at com.otaliastudios.transcoder.internal.transcode.DefaultTranscodeEngine$location$1.invoke(DefaultTranscodeEngine.kt:27)
        at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:210)
        at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:170)
        at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:194)
        at kotlin.sequences.SequencesKt___SequencesKt.firstOrNull(_Sequences.kt:133)
        at com.otaliastudios.transcoder.internal.transcode.DefaultTranscodeEngine.<init>(DefaultTranscodeEngine.kt:54)
        at com.otaliastudios.transcoder.internal.transcode.TranscodeEngine$Companion.transcode(TranscodeEngine.kt:32)
        at com.otaliastudios.transcoder.internal.transcode.TranscodeEngine.transcode(Unknown Source:2)
        at com.otaliastudios.transcoder.Transcoder$1.call(Transcoder.java:102)
        at com.otaliastudios.transcoder.Transcoder$1.call(Transcoder.java:99)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:923)
millsjustin commented 3 years ago

I attempted reproducing this on several versions. The results were as follows:

I'm not sure what changed to fix this but it seems to be resolved on main. That said, it still might be a good idea to catch exceptions thrown by mMetadata.extractMetadata(METADATA_KEY_LOCATION) as suggested by @daviduzan in https://github.com/natario1/Transcoder/issues/147

I would be happy to create that PR if you would like.

Noloxs commented 3 years ago

Do you have any guesstimates regarding the next official release?

As it isn't a blocking issue, we able to wait for it.

iNPUTmice commented 3 years ago

I can confirm that this appears to be fixed in main.

Edit: The MediaMetadataRetriever was released prematurely here:

at com.otaliastudios.transcoder.source.DefaultDataSource.deinitialize(DefaultDataSource.java:101)
    at com.otaliastudios.transcoder.source.DataSourceWrapper.deinitialize(DataSourceWrapper.java:102)
    at com.otaliastudios.transcoder.source.TrimDataSource.deinitialize(TrimDataSource.java:44)
    at com.otaliastudios.transcoder.internal.DataSources.deinit(DataSources.kt:23)
    at com.otaliastudios.transcoder.internal.DataSources.deinit(DataSources.kt:25)
    at com.otaliastudios.transcoder.internal.DataSources.<init>(DataSources.kt:44)
    at com.otaliastudios.transcoder.internal.DataSources.<init>(DataSources.kt:17)
    at com.otaliastudios.transcoder.internal.transcode.TranscodeEngine$Companion.transcode(TranscodeEngine.kt:33)
    at com.otaliastudios.transcoder.internal.transcode.TranscodeEngine.transcode(Unknown Source:2)
    at com.otaliastudios.transcoder.Transcoder$1.call(Transcoder.java:102)
    at com.otaliastudios.transcoder.Transcoder$1.call(Transcoder.java:99)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:923)

ba8f098c9461a12da1c11d26c9f32feb1e69b4d5 fixes this.

Could you @natario1 release main as 0.10.4

The try catch proposed in #147 should not be necessary.

natario1 commented 3 years ago

Done!