Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
jmir1 committed Aug 24, 2023
2 parents d2936d2 + 3ada286 commit c1eb18a
Show file tree
Hide file tree
Showing 39 changed files with 520 additions and 271 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ addons:
- ninja-build
- python3-pip
- python3-setuptools
- openjdk-17-jdk-headless

env:
global:
- JAVA_HOME: '/usr/lib/jvm/java-17-openjdk-amd64'

before_install:
- sudo pip3 install meson
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mpv-android is a video player for Android based on [libmpv](https://github.com/m
* Hardware and software video decoding
* Gesture-based seeking, volume/brightness control and more
* libass support for styled subtitles
* Secondary (or dual) subtitle support
* Advanced video settings (interpolation, debanding, scalers, ...)
* Play network streams with the "Open URL" function
* Background playback, Picture-in-Picture, keyboard input supported
Expand Down
16 changes: 13 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
versionCode 30
versionCode 31
versionName "1.9.n"
vectorDrawables.useSupportLibrary = true
}
Expand Down Expand Up @@ -43,6 +43,16 @@ android {
}
}

// https://youtrack.jetbrains.com/issue/KT-55947
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
kotlinOptions.jvmTarget = JavaVersion.VERSION_11.toString()
}
}

packagingOptions {
exclude '**/libavcodec.so'
exclude '**/libavdevice.so'
Expand All @@ -57,8 +67,8 @@ android {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.recyclerview:recyclerview:1.3.1'
implementation 'androidx.media:media:1.6.0'
}

Expand Down
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="is.xyz.mpv"
android:versionCode="30"
android:versionName="1.9n" >
android:versionCode="31"
android:versionName="1.9.n" >
<application/>
</manifest>
283 changes: 93 additions & 190 deletions app/src/main/assets/cacert.pem

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
recyclerView = (RecyclerView) view.findViewById(android.R.id.list);
// improve performance if you know that changes in content
// do not change the size of the RecyclerView
//noinspection InvalidSetHasFixedSize
recyclerView.setHasFixedSize(true);
// use a linear layout manager
layoutManager = new LinearLayoutManager(getActivity());
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/is/xyz/mpv/KeyMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class KeyMapping {
map.put(KEYCODE_F7, "F7");
map.put(KEYCODE_F8, "F8");
map.put(KEYCODE_F9, "F9");
map.put(KEYCODE_F10, "F10");
map.put(KEYCODE_F11, "F11");
map.put(KEYCODE_F12, "F12");

Expand Down
85 changes: 58 additions & 27 deletions app/src/main/java/is/xyz/mpv/MPVActivity.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package `is`.xyz.mpv

import `is`.xyz.mpv.databinding.PlayerBinding

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
Expand All @@ -26,8 +25,8 @@ import android.util.DisplayMetrics
import android.util.Rational
import androidx.core.content.ContextCompat
import android.view.*
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Button
import android.widget.RelativeLayout
import android.widget.SeekBar
import android.widget.Toast
import androidx.annotation.IdRes
Expand All @@ -38,6 +37,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.updateLayoutParams
import java.io.File
import java.lang.IllegalArgumentException
import kotlin.math.roundToInt
Expand Down Expand Up @@ -212,11 +212,16 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
}

ViewCompat.setOnApplyWindowInsetsListener(binding.outside) { _, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.tappableElement())
val lp = binding.outside.layoutParams as RelativeLayout.LayoutParams
lp.leftMargin = insets.left
lp.bottomMargin = insets.bottom
lp.rightMargin = insets.right
// guidance: https://medium.com/androiddevelopers/gesture-navigation-handling-visual-overlaps-4aed565c134c
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val insets2 = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
binding.outside.updateLayoutParams<MarginLayoutParams> {
// avoid system bars and cutout
leftMargin = Math.max(insets.left, insets2.left)
topMargin = Math.max(insets.top, insets2.top)
bottomMargin = Math.max(insets.bottom, insets2.bottom)
rightMargin = Math.max(insets.right, insets2.right)
}
WindowInsetsCompat.CONSUMED
}
}
Expand Down Expand Up @@ -316,6 +321,8 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
private fun finishWithResult(code: Int, includeTimePos: Boolean = false) {
// Refer to http://mpv-android.github.io/mpv-android/intent.html
// FIXME: should track end-file events to accurately report OK vs CANCELED
if (isFinishing) // only count first call
return
val result = Intent(RESULT_INTENT)
result.data = if (intent.data?.scheme == "file") null else intent.data
if (includeTimePos) {
Expand All @@ -329,6 +336,9 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
override fun onDestroy() {
Log.v(TAG, "Exiting.")

// Suppress any further callbacks
activityIsForeground = false

mediaSession?.isActive = false
mediaSession?.release()

Expand Down Expand Up @@ -406,6 +416,8 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
eventUiHandler.removeCallbacksAndMessages(null)
if (isFinishing) {
savePosition()
// tell mpv to shut down so that any other property changes or such are ignored,
// preventing useless busywork
MPVLib.command(arrayOf("stop"))
} else if (!shouldBackground) {
player.paused = true
Expand Down Expand Up @@ -711,12 +723,17 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
return true
}
KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DPAD_CENTER -> {
if (ev.action == KeyEvent.ACTION_DOWN) {
val childCount = group1.childCount
if (btnSelected < childCount)
group1.getChildAt(btnSelected)?.performClick()
val childCount = group1.childCount
val view = if (btnSelected < childCount)
group1.getChildAt(btnSelected)
else
group2.getChildAt(btnSelected - childCount)
if (ev.action == KeyEvent.ACTION_UP) {
// 500ms appears to be the standard
if (ev.eventTime - ev.downTime > 500L)
view?.performLongClick()
else
group2.getChildAt(btnSelected - childCount)?.performClick()
view?.performClick()
}
return true
}
Expand Down Expand Up @@ -812,20 +829,18 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
gestures.setMetrics(dm.widthPixels.toFloat(), dm.heightPixels.toFloat())

// Adjust control margins
run {
val lp = binding.controls.layoutParams as RelativeLayout.LayoutParams

lp.bottomMargin = if (!controlsAtBottom) {
Utils.convertDp(this, 60f)
binding.controls.updateLayoutParams<MarginLayoutParams> {
bottomMargin = if (!controlsAtBottom) {
Utils.convertDp(this@MPVActivity, 60f)
} else {
0
}
lp.leftMargin = if (!controlsAtBottom) {
Utils.convertDp(this, if (isLandscape) 60f else 24f)
leftMargin = if (!controlsAtBottom) {
Utils.convertDp(this@MPVActivity, if (isLandscape) 60f else 24f)
} else {
0
}
lp.rightMargin = lp.leftMargin
rightMargin = leftMargin
}
}

Expand Down Expand Up @@ -875,7 +890,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
val filepath = when (data.scheme) {
"file" -> data.path
"content" -> openContentFd(data)
"http", "https", "rtmp", "rtmps", "rtp", "rtsp", "mms", "mmst", "mmsh", "tcp", "udp"
"http", "https", "rtmp", "rtmps", "rtp", "rtsp", "mms", "mmst", "mmsh", "tcp", "udp", "lavf"
-> data.toString()
else -> null
}
Expand Down Expand Up @@ -903,7 +918,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
return path
}
// Else, pass the fd to mpv
return "fdclose://${fd}"
return "fd://${fd}"
}

private fun parseIntentExtras(extras: Bundle?) {
Expand Down Expand Up @@ -981,7 +996,26 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse

private fun pickAudio() = selectTrack("audio", { player.aid }, { player.aid = it })

private fun pickSub() = selectTrack("sub", { player.sid }, { player.sid = it })
private fun pickSub() {
val restore = pauseForDialog()
val impl = SubTrackDialog(player)
lateinit var dialog: AlertDialog
impl.listener = { it, secondary ->
if (secondary)
player.secondarySid = it.mpvId
else
player.sid = it.mpvId
dialog.dismiss()
trackSwitchNotification { TrackData(it.mpvId, SubTrackDialog.TRACK_TYPE) }
}

dialog = with (AlertDialog.Builder(this)) {
setView(impl.buildView(layoutInflater))
setOnDismissListener { restore() }
create()
}
dialog.show()
}

private fun openPlaylistMenu(restore: StateRestoreCallback) {
val impl = PlaylistDialog(player)
Expand Down Expand Up @@ -1640,10 +1674,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse
}

override fun event(eventId: Int) {
// finish on idle/shutdown
if (playbackHasStarted && eventId == MPVLib.mpvEventId.MPV_EVENT_IDLE)
finishWithResult(RESULT_OK)
else if (eventId == MPVLib.mpvEventId.MPV_EVENT_SHUTDOWN)
if (eventId == MPVLib.mpvEventId.MPV_EVENT_SHUTDOWN)
finishWithResult(if (playbackHasStarted) RESULT_OK else RESULT_CANCELED)

if (eventId == MPVLib.mpvEventId.MPV_EVENT_START_FILE) {
Expand Down
23 changes: 14 additions & 9 deletions app/src/main/java/is/xyz/mpv/MPVView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ class MPVView(context: Context, attrs: AttributeSet) : SurfaceView(context, attr
MPVLib.setOptionString("config-dir", configDir)
initOptions() // do this before init() so user-supplied config can override our choices
MPVLib.init()
// certain options are hardcoded:
/* Hardcoded options: */
// we need to call write-watch-later manually
MPVLib.setOptionString("save-position-on-quit", "no")
// would crash before the surface is attached
MPVLib.setOptionString("force-window", "no")
// "no" wouldn't work and "yes" is not intended by the UI
MPVLib.setOptionString("idle", "once")

holder.addCallback(this)
observeProperties()
Expand Down Expand Up @@ -104,7 +108,7 @@ class MPVView(context: Context, attrs: AttributeSet) : SurfaceView(context, attr
MPVLib.setOptionString("gpu-context", "android")
MPVLib.setOptionString("opengl-es", "yes")
MPVLib.setOptionString("hwdec", hwdec)
MPVLib.setOptionString("hwdec-codecs", "h264,hevc,mpeg4,mpeg2video,vp8,vp9")
MPVLib.setOptionString("hwdec-codecs", "h264,hevc,mpeg4,mpeg2video,vp8,vp9,av1")
MPVLib.setOptionString("ao", "audiotrack,opensles")
MPVLib.setOptionString("input-default-bindings", "yes")
// Limit demuxer cache since the defaults are too high for mobile devices
Expand Down Expand Up @@ -318,23 +322,24 @@ class MPVView(context: Context, attrs: AttributeSet) : SurfaceView(context, attr
val videoAspect: Double?
get() = MPVLib.getPropertyDouble("video-params/aspect")

class TrackDelegate {
class TrackDelegate(private val name: String) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
val v = MPVLib.getPropertyString(property.name)
val v = MPVLib.getPropertyString(name)
// we can get null here for "no" or other invalid value
return v?.toIntOrNull() ?: -1
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value == -1)
MPVLib.setPropertyString(property.name, "no")
MPVLib.setPropertyString(name, "no")
else
MPVLib.setPropertyInt(property.name, value)
MPVLib.setPropertyInt(name, value)
}
}

var vid: Int by TrackDelegate()
var sid: Int by TrackDelegate()
var aid: Int by TrackDelegate()
var vid: Int by TrackDelegate("vid")
var sid: Int by TrackDelegate("sid")
var secondarySid: Int by TrackDelegate("secondary-sid")
var aid: Int by TrackDelegate("aid")

// Commands

Expand Down
7 changes: 2 additions & 5 deletions app/src/main/java/is/xyz/mpv/PlaylistDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class PlaylistDialog(private val player: MPVView) {
selectedIndex = MPVLib.getPropertyInt("playlist-pos") ?: -1
playlist = player.loadPlaylist()
Log.v(TAG, "PlaylistDialog: loaded ${playlist.size} items")
(binding.list.adapter as RecyclerView.Adapter).notifyDataSetChanged()
binding.list.adapter!!.notifyDataSetChanged()
binding.list.scrollToPosition(playlist.indexOfFirst { it.index == selectedIndex })

val accent = ContextCompat.getColor(binding.root.context, R.color.accent)
Expand Down Expand Up @@ -84,12 +84,11 @@ class PlaylistDialog(private val player: MPVView) {
class ViewHolder(private val parent: PlaylistDialog, view: View) :
RecyclerView.ViewHolder(view) {
private val textView: TextView
var selfPosition: Int = -1

init {
textView = view.findViewById(android.R.id.text1)
view.setOnClickListener {
parent.clickItem(selfPosition)
parent.clickItem(bindingAdapterPosition)
}
}

Expand All @@ -102,12 +101,10 @@ class PlaylistDialog(private val player: MPVView) {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.dialog_playlist_item, viewGroup, false)

return ViewHolder(parent, view)
}

override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
viewHolder.selfPosition = position
val item = parent.playlist[position]
viewHolder.bind(item, item.index == parent.selectedIndex)
}
Expand Down
Loading

0 comments on commit c1eb18a

Please sign in to comment.