-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Open
Labels
Description
Expected behavior of the wanted feature
In order to select the appropriate caching and playback strategy, we need to know whether the opened media is a live stream. However, there seems to be no reliable way to achieve this. I tried a few combinations, but there is still a certain probability of false positives. For example:
// Heuristic live detection combining multiple mpv signals to avoid relying only on duration==0
static bool video_controller_detect_live(VideoController* self) {
// 1) duration unknown or zero is a hint but not decisive
double duration = 0.0;
const int dur_rc = mpv_get_property(self->mpv, "duration/full", MPV_FORMAT_DOUBLE, &duration);
const bool duration_unknown = dur_rc < 0; // unavailable
const bool duration_zero = (!duration_unknown && duration == 0.0);
// 2) seekability
gboolean seekable = FALSE;
const int sk_rc = mpv_get_property(self->mpv, "seekable", MPV_FORMAT_FLAG, &seekable);
gboolean partially_seekable = FALSE;
const int psk_rc = mpv_get_property(self->mpv, "partially-seekable", MPV_FORMAT_FLAG, &partially_seekable);
// 3) network source
gboolean via_network = FALSE;
mpv_get_property(self->mpv, "demuxer-via-network", MPV_FORMAT_FLAG, &via_network);
// 4) presence of HLS bitrate on any track implies HLS (often live when playlist type=EVENT)
bool has_hls_indicator = false;
int64_t tcount = 0;
if (mpv_get_property(self->mpv, "track-list/count", MPV_FORMAT_INT64, &tcount) == 0) {
for (int64_t i = 0; i < tcount; i++) {
char key[48];
snprintf(key, sizeof(key), "track-list/%ld/hls-bitrate", (long)i);
int64_t br = 0;
if (mpv_get_property(self->mpv, key, MPV_FORMAT_INT64, &br) == 0) {
has_hls_indicator = true;
break;
}
}
}
// 5) DASH hint: URL contains .mpd
bool url_looks_dash = false;
if (self->source) {
const char* ext = strstr(self->source, ".mpd");
url_looks_dash = (ext != NULL);
}
// 6) demuxer-start-time: often large/non-zero for live (DASH/HLS timebase)
double demux_start = 0.0;
const int dst_rc = mpv_get_property(self->mpv, "demuxer-start-time", MPV_FORMAT_DOUBLE, &demux_start);
// 7) demuxer-cache-state: bof-cached/eof-cached flags
bool bof_cached = true; // default assume true if unavailable
//bool eof_cached = false; // default assume false if unavailable
mpv_node cache;
if (mpv_get_property(self->mpv, "demuxer-cache-state", MPV_FORMAT_NODE, &cache) == 0) {
if (cache.format == MPV_FORMAT_NODE_MAP && cache.u.list) {
for (int i = 0; i < cache.u.list->num; i++) {
const char* key = cache.u.list->keys[i];
const mpv_node* val = &cache.u.list->values[i];
if (val->format == MPV_FORMAT_FLAG) {
if (g_strcmp0(key, "bof-cached") == 0) bof_cached = val->u.flag;
//if (g_strcmp0(key, "eof-cached") == 0) eof_cached = val->u.flag;
}
}
}
mpv_free_node_contents(&cache);
}
// Core rules:
// - If not seekable or only partially seekable over network, likely live
// - If duration is unknown and source is network, likely live
// - If HLS indicated and not fully seekable (or duration unknown/zero), likely live
const bool seek_info_known = (sk_rc == 0) || (psk_rc == 0);
const bool not_fully_seekable = (sk_rc == 0 && !seekable) || (psk_rc == 0 && partially_seekable);
if (via_network && (duration_unknown || not_fully_seekable)) {
return true;
}
if (has_hls_indicator && (duration_unknown || duration_zero || not_fully_seekable)) {
return true;
}
// DASH-oriented hints: network + .mpd + (non-BOF cached or significant non-zero demux start)
if (via_network && url_looks_dash && (!bof_cached || (dst_rc == 0 && demux_start > 30.0))) {
return true;
}
// Fallback: if everything is unknown but duration reported as 0 and it's a network stream
if (via_network && duration_zero && !seek_info_known) {
return true;
}
return false;
}
Alternative behavior of the wanted feature
No response
Log File
No response
Sample Files
No response