Beter end-of-file events

This commit is contained in:
Damontecres 2025-11-10 09:24:20 -05:00
parent 2ef095f73f
commit b84166e2a5
No known key found for this signature in database
5 changed files with 121 additions and 16 deletions

View file

@ -170,6 +170,18 @@ object MPVLib {
} }
} }
@JvmStatic
fun eventEndFile(
reason: Int,
error: Int,
) {
synchronized(observers) {
for (o in observers) {
o.eventEndFile(reason, error)
}
}
}
private val log_observers = mutableListOf<LogObserver>() private val log_observers = mutableListOf<LogObserver>()
@JvmStatic @JvmStatic
@ -223,6 +235,11 @@ object MPVLib {
) )
fun event(eventId: Int) fun event(eventId: Int)
fun eventEndFile(
reason: Int,
error: Int,
)
} }
interface LogObserver { interface LogObserver {
@ -282,4 +299,36 @@ object MPVLib {
const val MPV_LOG_LEVEL_DEBUG: Int = 60 const val MPV_LOG_LEVEL_DEBUG: Int = 60
const val MPV_LOG_LEVEL_TRACE: Int = 70 const val MPV_LOG_LEVEL_TRACE: Int = 70
} }
object MpvEndFileReason {
const val MPV_END_FILE_REASON_EOF: Int = 0
const val MPV_END_FILE_REASON_STOP: Int = 2
const val MPV_END_FILE_REASON_QUIT: Int = 3
const val MPV_END_FILE_REASON_ERROR: Int = 4
const val MPV_END_FILE_REASON_REDIRECT: Int = 5
}
object MpvError {
const val MPV_ERROR_SUCCESS = 0
const val MPV_ERROR_EVENT_QUEUE_FULL = -1
const val MPV_ERROR_NOMEM = -2
const val MPV_ERROR_UNINITIALIZED = -3
const val MPV_ERROR_INVALID_PARAMETER = -4
const val MPV_ERROR_OPTION_NOT_FOUND = -5
const val MPV_ERROR_OPTION_FORMAT = -6
const val MPV_ERROR_OPTION_ERROR = -7
const val MPV_ERROR_PROPERTY_NOT_FOUND = -8
const val MPV_ERROR_PROPERTY_FORMAT = -9
const val MPV_ERROR_PROPERTY_UNAVAILABLE = -10
const val MPV_ERROR_PROPERTY_ERROR = -11
const val MPV_ERROR_COMMAND = -12
const val MPV_ERROR_LOADING_FAILED = -13
const val MPV_ERROR_AO_INIT_FAILED = -14
const val MPV_ERROR_VO_INIT_FAILED = -15
const val MPV_ERROR_NOTHING_TO_PLAY = -16
const val MPV_ERROR_UNKNOWN_FORMAT = -17
const val MPV_ERROR_UNSUPPORTED = -18
const val MPV_ERROR_NOT_IMPLEMENTED = -19
const val MPV_ERROR_GENERIC = -20
}
} }

View file

@ -35,6 +35,9 @@ import androidx.media3.common.util.Util
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.media3.exoplayer.trackselection.TrackSelector import androidx.media3.exoplayer.trackselection.TrackSelector
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEndFileReason.MPV_END_FILE_REASON_EOF
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEndFileReason.MPV_END_FILE_REASON_ERROR
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEndFileReason.MPV_END_FILE_REASON_STOP
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_AUDIO_RECONFIG import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_AUDIO_RECONFIG
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_END_FILE import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_END_FILE
import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_FILE_LOADED import com.github.damontecres.wholphin.util.mpv.MPVLib.MpvEvent.MPV_EVENT_FILE_LOADED
@ -83,6 +86,7 @@ class MpvPlayer(
private var positionMs: Long = -1L private var positionMs: Long = -1L
private var playbackState: Int = STATE_READY private var playbackState: Int = STATE_READY
@Volatile
var isReleased = false var isReleased = false
private set private set
@ -277,9 +281,15 @@ class MpvPlayer(
} }
override fun stop() { override fun stop() {
throwIfReleased()
if (DEBUG) Timber.v("stop") if (DEBUG) Timber.v("stop")
throwIfReleased()
pause() pause()
mediaItem = null
positionMs = -1L
durationMs = 0L
playbackState = STATE_IDLE
notifyListeners(EVENT_IS_PLAYING_CHANGED) { onIsPlayingChanged(false) }
notifyListeners(EVENT_PLAYBACK_STATE_CHANGED) { onPlaybackStateChanged(STATE_IDLE) }
} }
override fun release() { override fun release() {
@ -294,6 +304,7 @@ class MpvPlayer(
override fun getCurrentTracks(): Tracks { override fun getCurrentTracks(): Tracks {
if (DEBUG) Timber.v("getCurrentTracks") if (DEBUG) Timber.v("getCurrentTracks")
throwIfReleased()
return getTracks() return getTracks()
} }
@ -307,6 +318,7 @@ class MpvPlayer(
override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) { override fun setTrackSelectionParameters(parameters: TrackSelectionParameters) {
Timber.v("TrackSelection: setTrackSelectionParameters %s", parameters) Timber.v("TrackSelection: setTrackSelectionParameters %s", parameters)
throwIfReleased()
val tracks = getTracks() val tracks = getTracks()
if (C.TRACK_TYPE_TEXT in parameters.disabledTrackTypes) { if (C.TRACK_TYPE_TEXT in parameters.disabledTrackTypes) {
// Subtitles disabled // Subtitles disabled
@ -352,7 +364,7 @@ class MpvPlayer(
override fun getMediaMetadata(): MediaMetadata { override fun getMediaMetadata(): MediaMetadata {
if (DEBUG) Timber.v("getMediaMetadata") if (DEBUG) Timber.v("getMediaMetadata")
return mediaItem!!.mediaMetadata return MediaMetadata.EMPTY
} }
override fun getPlaylistMetadata(): MediaMetadata { override fun getPlaylistMetadata(): MediaMetadata {
@ -408,6 +420,7 @@ class MpvPlayer(
override fun getTotalBufferedDuration(): Long { override fun getTotalBufferedDuration(): Long {
if (DEBUG) Timber.v("getTotalBufferedDuration") if (DEBUG) Timber.v("getTotalBufferedDuration")
if (isReleased) return 0
return MPVLib.getPropertyDouble("demuxer-cache-duration")?.seconds?.inWholeMilliseconds ?: 0 return MPVLib.getPropertyDouble("demuxer-cache-duration")?.seconds?.inWholeMilliseconds ?: 0
} }
@ -426,8 +439,7 @@ class MpvPlayer(
override fun getAudioAttributes(): AudioAttributes = throw UnsupportedOperationException() override fun getAudioAttributes(): AudioAttributes = throw UnsupportedOperationException()
override fun setVolume(volume: Float) { override fun setVolume(volume: Float): Unit = throw UnsupportedOperationException()
}
override fun getVolume(): Float = 1f override fun getVolume(): Float = 1f
@ -622,16 +634,7 @@ class MpvPlayer(
MPV_EVENT_END_FILE -> { MPV_EVENT_END_FILE -> {
Timber.d("event: MPV_EVENT_END_FILE") Timber.d("event: MPV_EVENT_END_FILE")
notifyListeners(EVENT_IS_PLAYING_CHANGED) { onIsPlayingChanged(false) } // Handled by eventEndFile
val curPos = MPVLib.getPropertyDouble("time-pos/full")
Timber.v("MPV_EVENT_END_FILE: positionMs=$positionMs, durationMs=$durationMs, curPos=$curPos")
if (positionMs >= (durationMs - 1000) && curPos == null) {
playbackState = STATE_ENDED
notifyListeners(EVENT_PLAYBACK_STATE_CHANGED) {
onPlaybackStateChanged(STATE_ENDED)
}
}
} }
else -> { else -> {
@ -640,6 +643,42 @@ class MpvPlayer(
} }
} }
override fun eventEndFile(
reason: Int,
error: Int,
) {
Timber.d("MPV_EVENT_END_FILE: %s %s", reason, error)
notifyListeners(EVENT_IS_PLAYING_CHANGED) { onIsPlayingChanged(false) }
when (reason) {
MPV_END_FILE_REASON_EOF -> {
notifyListeners(EVENT_PLAYBACK_STATE_CHANGED) {
onPlaybackStateChanged(STATE_ENDED)
}
}
MPV_END_FILE_REASON_STOP -> {
// User initiated (eg stop, play next, etc)
}
MPV_END_FILE_REASON_ERROR -> {
Timber.e("libmpv error, error=%s", error)
notifyListeners(EVENT_PLAYER_ERROR) {
onPlayerError(
PlaybackException(
"libmpv error",
null,
error,
),
)
}
}
else -> {
// no-op
}
}
}
private fun loadFile() { private fun loadFile() {
val url = mediaItem!!.localConfiguration?.uri.toString() val url = mediaItem!!.localConfiguration?.uri.toString()
if (startPositionMs > 0) { if (startPositionMs > 0) {
@ -743,8 +782,14 @@ class MpvPlayer(
} }
var subtitleDelay: Double var subtitleDelay: Double
get() = MPVLib.getPropertyDouble("sub-delay") ?: 0.0 get() {
set(value) = MPVLib.setPropertyDouble("sub-delay", value) throwIfReleased()
return MPVLib.getPropertyDouble("sub-delay") ?: 0.0
}
set(value) {
throwIfReleased()
MPVLib.setPropertyDouble("sub-delay", value)
}
} }
fun MPVLib.setPropertyColor( fun MPVLib.setPropertyColor(

View file

@ -45,6 +45,11 @@ static void sendEventToJava(JNIEnv *env, int event)
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_event, event); env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_event, event);
} }
static void sendEndFileEventToJava(JNIEnv *env, mpv_event_end_file *event)
{
env->CallStaticVoidMethod(mpv_MPVLib, mpv_MPVLib_end_file_event, event->reason, event->error);
}
static void sendLogMessageToJava(JNIEnv *env, mpv_event_log_message *msg) static void sendLogMessageToJava(JNIEnv *env, mpv_event_log_message *msg)
{ {
// filter the most obvious cases of invalid utf-8, since Java would choke on it // filter the most obvious cases of invalid utf-8, since Java would choke on it
@ -79,6 +84,7 @@ void *event_thread(void *arg)
mpv_event *mp_event; mpv_event *mp_event;
mpv_event_property *mp_property = NULL; mpv_event_property *mp_property = NULL;
mpv_event_log_message *msg = NULL; mpv_event_log_message *msg = NULL;
mpv_event_end_file *mp_end_file = NULL;
mp_event = mpv_wait_event(g_mpv, -1.0); mp_event = mpv_wait_event(g_mpv, -1.0);
@ -98,6 +104,9 @@ void *event_thread(void *arg)
mp_property = (mpv_event_property*)mp_event->data; mp_property = (mpv_event_property*)mp_event->data;
sendPropertyUpdateToJava(env, mp_property); sendPropertyUpdateToJava(env, mp_property);
break; break;
case MPV_EVENT_END_FILE:
mp_end_file = (mpv_event_end_file*)mp_event->data;
sendEndFileEventToJava(env, mp_end_file);
default: default:
ALOGV("event: %s\n", mpv_event_name(mp_event->event_id)); ALOGV("event: %s\n", mpv_event_name(mp_event->event_id));
sendEventToJava(env, mp_event->event_id); sendEventToJava(env, mp_event->event_id);

View file

@ -44,6 +44,7 @@ void init_methods_cache(JNIEnv *env)
mpv_MPVLib_eventProperty_Sd = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;D)V"); // eventProperty(String, double) mpv_MPVLib_eventProperty_Sd = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;D)V"); // eventProperty(String, double)
mpv_MPVLib_eventProperty_SS = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String) mpv_MPVLib_eventProperty_SS = env->GetStaticMethodID(mpv_MPVLib, "eventProperty", "(Ljava/lang/String;Ljava/lang/String;)V"); // eventProperty(String, String)
mpv_MPVLib_event = env->GetStaticMethodID(mpv_MPVLib, "event", "(I)V"); // event(int) mpv_MPVLib_event = env->GetStaticMethodID(mpv_MPVLib, "event", "(I)V"); // event(int)
mpv_MPVLib_end_file_event = env->GetStaticMethodID(mpv_MPVLib, "eventEndFile", "(II)V"); // eventEndFile(int, int)
mpv_MPVLib_logMessage_SiS = env->GetStaticMethodID(mpv_MPVLib, "logMessage", "(Ljava/lang/String;ILjava/lang/String;)V"); // logMessage(String, int, String) mpv_MPVLib_logMessage_SiS = env->GetStaticMethodID(mpv_MPVLib, "logMessage", "(Ljava/lang/String;ILjava/lang/String;)V"); // logMessage(String, int, String)
#undef FIND_CLASS #undef FIND_CLASS

View file

@ -26,4 +26,5 @@ UTIL_EXTERN jmethodID mpv_MPVLib_eventProperty_S,
mpv_MPVLib_eventProperty_Sd, mpv_MPVLib_eventProperty_Sd,
mpv_MPVLib_eventProperty_SS, mpv_MPVLib_eventProperty_SS,
mpv_MPVLib_event, mpv_MPVLib_event,
mpv_MPVLib_end_file_event,
mpv_MPVLib_logMessage_SiS; mpv_MPVLib_logMessage_SiS;