From af3cfc46bf9e08ee924a24ab54e64fa64d3d6eab Mon Sep 17 00:00:00 2001 From: tibbi Date: Wed, 2 Jan 2019 23:56:09 +0100 Subject: [PATCH] improve panorama video handling at third party intents --- .../pro/activities/PhotoVideoActivity.kt | 29 +++++++-- .../gallery/pro/extensions/Context.kt | 60 +++++++++++++++++ .../gallery/pro/fragments/VideoFragment.kt | 64 +------------------ 3 files changed, 88 insertions(+), 65 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt index ec1e4ebf1..b5b1c0fff 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/activities/PhotoVideoActivity.kt @@ -27,6 +27,7 @@ import com.simplemobiletools.gallery.pro.models.Medium import kotlinx.android.synthetic.main.bottom_actions.* import kotlinx.android.synthetic.main.fragment_holder.* import java.io.File +import java.io.FileInputStream open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentListener { @@ -147,10 +148,30 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList return } - val mimeType = getUriMimeType(mUri.toString(), newUri) - Intent(this, VideoPlayerActivity::class.java).apply { - setDataAndType(newUri, mimeType) - startActivity(this) + var isPanorama = false + val realPath = intent?.extras?.getString(REAL_FILE_PATH) ?: "" + try { + if (realPath.isNotEmpty()) { + val fis = FileInputStream(File(realPath)) + parseFileChannel(realPath, fis.channel, 0, 0, 0) { + isPanorama = true + } + } + } catch (ignored: Exception) { + } catch (ignored: OutOfMemoryError) { + } + + if (isPanorama) { + Intent(applicationContext, PanoramaVideoActivity::class.java).apply { + putExtra(PATH, realPath) + startActivity(this) + } + } else { + val mimeType = getUriMimeType(mUri.toString(), newUri) + Intent(applicationContext, VideoPlayerActivity::class.java).apply { + setDataAndType(newUri, mimeType) + startActivity(this) + } } finish() } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt index 96ae9ab89..3f6b336de 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt @@ -35,6 +35,9 @@ import com.simplemobiletools.gallery.pro.svg.SvgSoftwareLayerSetter import com.simplemobiletools.gallery.pro.views.MySquareImageView import pl.droidsonroids.gif.GifDrawable import java.io.File +import java.io.FileInputStream +import java.nio.ByteBuffer +import java.nio.channels.FileChannel import java.util.HashSet import java.util.LinkedHashSet import kotlin.Comparator @@ -623,3 +626,60 @@ fun Context.updateWidgets() { } } } + +// based on https://github.com/sannies/mp4parser/blob/master/examples/src/main/java/com/google/code/mp4parser/example/PrintStructure.java +fun Context.parseFileChannel(path: String, fc: FileChannel, level: Int, start: Long, end: Long, callback: () -> Unit) { + val FILE_CHANNEL_CONTAINERS = arrayListOf("moov", "trak", "mdia", "minf", "udta", "stbl") + 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(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 + } + } + + val xmlString = sb.toString().toLowerCase() + if (xmlString.contains("gspherical:projectiontype>equirectangular") || xmlString.contains("gspherical:projectiontype=\"equirectangular\"")) { + callback.invoke() + } + return + } + + if (FILE_CHANNEL_CONTAINERS.contains(type)) { + parseFileChannel(path, fc, level + 1, begin + 8, newEnd, callback) + } + + fc.position(newEnd) + } + } catch (ignored: Exception) { + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt index ec58f51d5..586edfaa4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/fragments/VideoFragment.kt @@ -11,7 +11,6 @@ import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.gallery.pro.R import com.simplemobiletools.gallery.pro.activities.PanoramaVideoActivity import com.simplemobiletools.gallery.pro.extensions.* -import com.simplemobiletools.gallery.pro.helpers.IsoTypeReader import com.simplemobiletools.gallery.pro.helpers.MEDIUM import com.simplemobiletools.gallery.pro.helpers.PATH import com.simplemobiletools.gallery.pro.models.Medium @@ -19,12 +18,8 @@ import com.simplemobiletools.gallery.pro.views.MediaSideScroll 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() { - private val FILE_CHANNEL_CONTAINERS = arrayListOf("moov", "trak", "mdia", "minf", "udta", "stbl") - private var mIsFullscreen = false private var mWasFragmentInit = false private var mIsPanorama = false @@ -168,67 +163,14 @@ class VideoFragment : ViewPagerFragment() { private fun checkIfPanorama() { try { val fis = FileInputStream(File(medium.path)) - parseFileChannel(fis.channel, 0, 0, 0) + context!!.parseFileChannel(medium.path, fis.channel, 0, 0, 0) { + mIsPanorama = true + } } 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 - } - } - - val xmlString = sb.toString().toLowerCase() - mIsPanorama = xmlString.contains("gspherical:projectiontype>equirectangular") || - xmlString.contains("gspherical:projectiontype=\"equirectangular\"") - 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)