From 6d0c5fdf65012d1533090475a0a719805494861c Mon Sep 17 00:00:00 2001 From: tibbi Date: Tue, 30 Oct 2018 19:19:32 +0100 Subject: [PATCH] improving the panoramic video check, do not rely on 2:1 aspect ratio --- app/build.gradle | 2 +- .../gallery/fragments/VideoFragment.kt | 68 ++++++++++++++++++- .../gallery/helpers/IsoTypeReader.kt | 27 ++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/gallery/helpers/IsoTypeReader.kt diff --git a/app/build.gradle b/app/build.gradle index d2a4740fc..7db11603b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ android { } dependencies { - implementation 'com.simplemobiletools:commons:5.2.13' + implementation 'com.simplemobiletools:commons:5.2.14' implementation 'com.theartofdev.edmodo:android-image-cropper:2.7.0' implementation 'androidx.multidex:multidex:2.0.0' implementation 'it.sephiroth.android.exif:library:1.0.1' diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt index 787bab494..8192b204e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/fragments/VideoFragment.kt @@ -36,9 +36,13 @@ import com.simplemobiletools.gallery.views.MediaSideScroll import kotlinx.android.synthetic.main.bottom_video_time_holder.view.* import kotlinx.android.synthetic.main.pager_video_item.view.* import java.io.File +import java.io.FileInputStream +import java.nio.ByteBuffer +import java.nio.channels.FileChannel class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener { private val PROGRESS = "progress" + private val FILE_CHANNEL_CONTAINERS = arrayListOf("moov", "trak", "mdia", "minf", "udta", "stbl") private var mTextureView: TextureView? = null private var mCurrTimeView: TextView? = null @@ -112,10 +116,11 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S mIsFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN initTimeHolder() + checkIfPanorama() + medium.path.getVideoResolution()?.apply { mVideoSize.x = x mVideoSize.y = y - mIsPanorama = x == y * 2 if (mIsPanorama) { mView.apply { panorama_outline.beVisible() @@ -628,6 +633,67 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S mIsDragged = false } + private fun checkIfPanorama() { + try { + val fis = FileInputStream(File(medium.path)) + parseFileChannel(fis.channel, 0, 0, 0) + } catch (ignored: Exception) { + } catch (ignored: OutOfMemoryError) { + } + } + + // based on https://github.com/sannies/mp4parser/blob/master/examples/src/main/java/com/google/code/mp4parser/example/PrintStructure.java + private fun parseFileChannel(fc: FileChannel, level: Int, start: Long, end: Long) { + try { + var iteration = 0 + var currEnd = end + fc.position(start) + if (currEnd <= 0) { + currEnd = start + fc.size() + } + + while (currEnd - fc.position() > 8) { + // just a check to avoid deadloop at some videos + if (iteration++ > 50) { + return + } + + val begin = fc.position() + val byteBuffer = ByteBuffer.allocate(8) + fc.read(byteBuffer) + byteBuffer.rewind() + val size = IsoTypeReader.readUInt32(byteBuffer) + val type = IsoTypeReader.read4cc(byteBuffer) + val newEnd = begin + size + + if (type == "uuid") { + val fis = FileInputStream(File(medium.path)) + fis.skip(begin) + + val sb = StringBuilder() + val buffer = ByteArray(1024) + while (true) { + val n = fis.read(buffer) + if (n != -1) { + sb.append(String(buffer, 0, n)) + } else { + break + } + } + mIsPanorama = sb.toString().contains("true") || sb.toString().contains("GSpherical:Spherical=\"True\"") + return + } + + if (FILE_CHANNEL_CONTAINERS.contains(type)) { + parseFileChannel(fc, level + 1, begin + 8, newEnd) + } + + fc.position(newEnd) + } + } catch (ignored: Exception) { + } + } + private fun openPanorama() { Intent(context, PanoramaVideoActivity::class.java).apply { putExtra(PATH, medium.path) diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/IsoTypeReader.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/IsoTypeReader.kt new file mode 100644 index 000000000..70d6f6afa --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/helpers/IsoTypeReader.kt @@ -0,0 +1,27 @@ +package com.simplemobiletools.gallery.helpers + +import java.io.UnsupportedEncodingException +import java.nio.ByteBuffer +import java.nio.charset.Charset + +// file taken from the https://github.com/sannies/mp4parser project, used at determining if a video is a panoramic one +object IsoTypeReader { + fun readUInt32(bb: ByteBuffer): Long { + var i = bb.int.toLong() + if (i < 0) { + i += 1L shl 32 + } + return i + } + + fun read4cc(bb: ByteBuffer): String? { + val codeBytes = ByteArray(4) + bb.get(codeBytes) + + return try { + String(codeBytes, Charset.forName("ISO-8859-1")) + } catch (e: UnsupportedEncodingException) { + null + } + } +}