summaryrefslogtreecommitdiffstats
path: root/demux/demux.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux.c')
-rw-r--r--demux/demux.c474
1 files changed, 260 insertions, 214 deletions
diff --git a/demux/demux.c b/demux/demux.c
index e4dda43cf0..5997a96ed6 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -15,20 +15,18 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <assert.h>
#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdatomic.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
-#include <unistd.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stdint.h>
-
-#include <math.h>
-
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "cache.h"
#include "config.h"
@@ -42,7 +40,6 @@
#include "common/stats.h"
#include "misc/charset_conv.h"
#include "misc/thread_tools.h"
-#include "osdep/atomic.h"
#include "osdep/timer.h"
#include "osdep/threads.h"
@@ -84,27 +81,6 @@ static const demuxer_desc_t *const demuxer_list[] = {
NULL
};
-struct demux_opts {
- int enable_cache;
- int disk_cache;
- int64_t max_bytes;
- int64_t max_bytes_bw;
- int donate_fw;
- double min_secs;
- int force_seekable;
- double min_secs_cache;
- int access_references;
- int seekable_cache;
- int create_ccs;
- char *record_file;
- int video_back_preroll;
- int audio_back_preroll;
- int back_batch[STREAM_TYPE_COUNT];
- double back_seek_size;
- char *meta_cp;
- int force_retry_eof;
-};
-
#define OPT_BASE_STRUCT struct demux_opts
static bool get_demux_sub_opts(int index, const struct m_sub_options **sub);
@@ -113,20 +89,23 @@ const struct m_sub_options demux_conf = {
.opts = (const struct m_option[]){
{"cache", OPT_CHOICE(enable_cache,
{"no", 0}, {"auto", -1}, {"yes", 1})},
- {"cache-on-disk", OPT_FLAG(disk_cache)},
+ {"cache-on-disk", OPT_BOOL(disk_cache)},
{"demuxer-readahead-secs", OPT_DOUBLE(min_secs), M_RANGE(0, DBL_MAX)},
+ {"demuxer-hysteresis-secs", OPT_DOUBLE(hyst_secs), M_RANGE(0, DBL_MAX)},
{"demuxer-max-bytes", OPT_BYTE_SIZE(max_bytes),
M_RANGE(0, M_MAX_MEM_BYTES)},
{"demuxer-max-back-bytes", OPT_BYTE_SIZE(max_bytes_bw),
M_RANGE(0, M_MAX_MEM_BYTES)},
- {"demuxer-donate-buffer", OPT_FLAG(donate_fw)},
- {"force-seekable", OPT_FLAG(force_seekable)},
- {"cache-secs", OPT_DOUBLE(min_secs_cache), M_RANGE(0, DBL_MAX),
- .deprecation_message = "will use unlimited time"},
- {"access-references", OPT_FLAG(access_references)},
+ {"demuxer-donate-buffer", OPT_BOOL(donate_fw)},
+ {"force-seekable", OPT_BOOL(force_seekable)},
+ {"cache-secs", OPT_DOUBLE(min_secs_cache), M_RANGE(0, DBL_MAX)},
+ {"access-references", OPT_BOOL(access_references)},
{"demuxer-seekable-cache", OPT_CHOICE(seekable_cache,
{"auto", -1}, {"no", 0}, {"yes", 1})},
- {"sub-create-cc-track", OPT_FLAG(create_ccs)},
+ {"index", OPT_CHOICE(index_mode, {"default", 1}, {"recreate", 0})},
+ {"mf-fps", OPT_DOUBLE(mf_fps)},
+ {"mf-type", OPT_STRING(mf_type)},
+ {"sub-create-cc-track", OPT_BOOL(create_ccs)},
{"stream-record", OPT_STRING(record_file)},
{"video-backward-overlap", OPT_CHOICE(video_back_preroll, {"auto", -1}),
M_RANGE(0, 1024)},
@@ -139,8 +118,6 @@ const struct m_sub_options demux_conf = {
{"demuxer-backward-playback-step", OPT_DOUBLE(back_seek_size),
M_RANGE(0, DBL_MAX)},
{"metadata-codepage", OPT_STRING(meta_cp)},
- {"demuxer-force-retry-on-eof", OPT_FLAG(force_retry_eof),
- .deprecation_message = "temporary debug option, no replacement"},
{0}
},
.size = sizeof(struct demux_opts),
@@ -148,11 +125,13 @@ const struct m_sub_options demux_conf = {
.enable_cache = -1, // auto
.max_bytes = 150 * 1024 * 1024,
.max_bytes_bw = 50 * 1024 * 1024,
- .donate_fw = 1,
+ .donate_fw = true,
.min_secs = 1.0,
.min_secs_cache = 1000.0 * 60 * 60,
.seekable_cache = -1,
- .access_references = 1,
+ .index_mode = 1,
+ .mf_fps = 1.0,
+ .access_references = true,
.video_back_preroll = -1,
.audio_back_preroll = -1,
.back_seek_size = 60,
@@ -160,7 +139,7 @@ const struct m_sub_options demux_conf = {
[STREAM_VIDEO] = 1,
[STREAM_AUDIO] = 10,
},
- .meta_cp = "utf-8",
+ .meta_cp = "auto",
},
.get_sub_options = get_demux_sub_opts,
};
@@ -180,15 +159,12 @@ struct demux_internal {
// The lock protects the packet queues (struct demux_stream),
// and the fields below.
- pthread_mutex_t lock;
- pthread_cond_t wakeup;
- pthread_t thread;
+ mp_mutex lock;
+ mp_cond wakeup;
+ mp_thread thread;
// -- All the following fields are protected by lock.
- struct demux_opts *opts;
- struct m_config_cache *opts_cache;
-
bool thread_terminate;
bool threading;
bool shutdown_async;
@@ -211,6 +187,8 @@ struct demux_internal {
bool warned_queue_overflow;
bool eof; // whether we're in EOF state
double min_secs;
+ double hyst_secs; // stop reading till there's hyst_secs remaining
+ bool hyst_active;
size_t max_bytes;
size_t max_bytes_bw;
bool seekable_cache;
@@ -389,7 +367,7 @@ struct demux_stream {
bool eager; // try to keep at least 1 packet queued
// if false, this stream is disabled, or passively
// read (like subtitles)
- bool still_image; // stream has still video images
+ bool still_image; // stream consists of multiple sparse still images
bool refreshing; // finding old position after track switches
bool eof; // end of demuxed stream? (true if no more packets)
@@ -452,7 +430,7 @@ struct demux_stream {
static void switch_to_fresh_cache_range(struct demux_internal *in);
static void demuxer_sort_chapters(demuxer_t *demuxer);
-static void *demux_thread(void *pctx);
+static MP_THREAD_VOID demux_thread(void *pctx);
static void update_cache(struct demux_internal *in);
static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp);
static struct demux_packet *advance_reader_head(struct demux_stream *ds);
@@ -468,7 +446,7 @@ static void prune_old_packets(struct demux_internal *in);
static void dumper_close(struct demux_internal *in);
static void demux_convert_tags_charset(struct demuxer *demuxer);
-static uint64_t get_foward_buffered_bytes(struct demux_stream *ds)
+static uint64_t get_forward_buffered_bytes(struct demux_stream *ds)
{
if (!ds->reader_head)
return 0;
@@ -538,7 +516,7 @@ static void check_queue_consistency(struct demux_internal *in)
// ...reader_head and others must be in the queue.
assert(is_forward == !!queue->ds->reader_head);
assert(kf_found == !!queue->keyframe_latest);
- uint64_t fw_bytes2 = get_foward_buffered_bytes(queue->ds);
+ uint64_t fw_bytes2 = get_forward_buffered_bytes(queue->ds);
assert(fw_bytes == fw_bytes2);
}
@@ -685,7 +663,7 @@ static void update_seek_ranges(struct demux_cached_range *range)
}
}
- if (range->seek_start >= range->seek_end)
+ if (range->seek_start >= range->seek_end && !(range->is_bof && range->is_eof))
goto broken;
prune_metadata(range);
@@ -878,7 +856,7 @@ static void wakeup_ds(struct demux_stream *ds)
ds->in->wakeup_cb(ds->in->wakeup_cb_ctx);
}
ds->need_wakeup = false;
- pthread_cond_signal(&ds->in->wakeup);
+ mp_cond_signal(&ds->in->wakeup);
}
}
@@ -941,9 +919,9 @@ static void update_stream_selection_state(struct demux_internal *in,
void demux_set_ts_offset(struct demuxer *demuxer, double offset)
{
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->ts_offset = offset;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
static void add_missing_streams(struct demux_internal *in,
@@ -975,6 +953,7 @@ struct sh_stream *demux_alloc_sh_stream(enum stream_type type)
.index = -1,
.ff_index = -1, // may be overwritten by demuxer
.demuxer_id = -1, // ... same
+ .program_id = -1, // ... same
.codec = talloc_zero(sh, struct mp_codec_params),
.tags = talloc_zero(sh, struct mp_tags),
};
@@ -1024,7 +1003,7 @@ static void demux_add_sh_stream_locked(struct demux_internal *in,
switch (ds->type) {
case STREAM_AUDIO:
- ds->back_preroll = in->opts->audio_back_preroll;
+ ds->back_preroll = in->d_user->opts->audio_back_preroll;
if (ds->back_preroll < 0) { // auto
ds->back_preroll = mp_codec_is_lossless(sh->codec->codec) ? 0 : 1;
if (sh->codec->codec && (strcmp(sh->codec->codec, "opus") == 0 ||
@@ -1034,7 +1013,7 @@ static void demux_add_sh_stream_locked(struct demux_internal *in,
}
break;
case STREAM_VIDEO:
- ds->back_preroll = in->opts->video_back_preroll;
+ ds->back_preroll = in->d_user->opts->video_back_preroll;
if (ds->back_preroll < 0)
ds->back_preroll = 0; // auto
break;
@@ -1056,9 +1035,9 @@ void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh)
{
struct demux_internal *in = demuxer->in;
assert(demuxer == in->d_thread);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
demux_add_sh_stream_locked(in, sh);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
// Return a stream with the given index. Since streams can only be added during
@@ -1068,10 +1047,10 @@ void demux_add_sh_stream(struct demuxer *demuxer, struct sh_stream *sh)
struct sh_stream *demux_get_stream(struct demuxer *demuxer, int index)
{
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
assert(index >= 0 && index < in->num_streams);
struct sh_stream *r = in->streams[index];
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return r;
}
@@ -1079,9 +1058,9 @@ struct sh_stream *demux_get_stream(struct demuxer *demuxer, int index)
int demux_get_num_stream(struct demuxer *demuxer)
{
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
int r = in->num_streams;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return r;
}
@@ -1120,8 +1099,8 @@ static void demux_dealloc(struct demux_internal *in)
{
for (int n = 0; n < in->num_streams; n++)
talloc_free(in->streams[n]);
- pthread_mutex_destroy(&in->lock);
- pthread_cond_destroy(&in->wakeup);
+ mp_mutex_destroy(&in->lock);
+ mp_cond_destroy(&in->wakeup);
talloc_free(in->d_user);
}
@@ -1152,11 +1131,11 @@ struct demux_free_async_state *demux_free_async(struct demuxer *demuxer)
if (!in->threading)
return NULL;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->thread_terminate = true;
in->shutdown_async = true;
- pthread_cond_signal(&in->wakeup);
- pthread_mutex_unlock(&in->lock);
+ mp_cond_signal(&in->wakeup);
+ mp_mutex_unlock(&in->lock);
return (struct demux_free_async_state *)demuxer->in; // lies
}
@@ -1180,9 +1159,9 @@ bool demux_free_async_finish(struct demux_free_async_state *state)
{
struct demux_internal *in = (struct demux_internal *)state; // reverse lies
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
bool busy = in->shutdown_async;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
if (busy)
return false;
@@ -1211,7 +1190,7 @@ void demux_start_thread(struct demuxer *demuxer)
if (!in->threading) {
in->threading = true;
- if (pthread_create(&in->thread, NULL, demux_thread, in))
+ if (mp_thread_create(&in->thread, demux_thread, in))
in->threading = false;
}
}
@@ -1222,11 +1201,11 @@ void demux_stop_thread(struct demuxer *demuxer)
assert(demuxer == in->d_user);
if (in->threading) {
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->thread_terminate = true;
- pthread_cond_signal(&in->wakeup);
- pthread_mutex_unlock(&in->lock);
- pthread_join(in->thread, NULL);
+ mp_cond_signal(&in->wakeup);
+ mp_mutex_unlock(&in->lock);
+ mp_thread_join(in->thread);
in->threading = false;
in->thread_terminate = false;
}
@@ -1236,10 +1215,10 @@ void demux_stop_thread(struct demuxer *demuxer)
void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *ctx)
{
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->wakeup_cb = cb;
in->wakeup_cb_ctx = ctx;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
void demux_start_prefetch(struct demuxer *demuxer)
@@ -1247,10 +1226,10 @@ void demux_start_prefetch(struct demuxer *demuxer)
struct demux_internal *in = demuxer->in;
assert(demuxer == in->d_user);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->reading = true;
- pthread_cond_signal(&in->wakeup);
- pthread_mutex_unlock(&in->lock);
+ mp_cond_signal(&in->wakeup);
+ mp_mutex_unlock(&in->lock);
}
const char *stream_type_name(enum stream_type type)
@@ -1273,6 +1252,8 @@ static struct sh_stream *demuxer_get_cc_track_locked(struct sh_stream *stream)
return NULL;
sh->codec->codec = "eia_608";
sh->default_track = true;
+ sh->hls_bitrate = stream->hls_bitrate;
+ sh->program_id = stream->program_id;
stream->ds->cc = sh;
demux_add_sh_stream_locked(stream->ds->in, sh);
sh->ds->ignore_eof = true;
@@ -1285,10 +1266,10 @@ void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp)
{
struct demux_internal *in = stream->ds->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
struct sh_stream *sh = demuxer_get_cc_track_locked(stream);
if (!sh) {
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
talloc_free(dp);
return;
}
@@ -1298,7 +1279,7 @@ void demuxer_feed_caption(struct sh_stream *stream, demux_packet_t *dp)
dp->dts = MP_ADD_PTS(dp->dts, -in->ts_offset);
dp->stream = sh->index;
add_packet_locked(sh, dp);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
static void error_on_backward_demuxing(struct demux_internal *in)
@@ -1335,8 +1316,8 @@ static void perform_backward_seek(struct demux_internal *in)
in->reading = true;
// Don't starve other threads.
- pthread_mutex_unlock(&in->lock);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_unlock(&in->lock);
+ mp_mutex_lock(&in->lock);
}
// For incremental backward demuxing search work.
@@ -1460,7 +1441,7 @@ static void find_backward_restart_pos(struct demux_stream *ds)
// Number of renderable keyframes to return to user.
// (Excludes preroll, which is decoded by user, but then discarded.)
- int batch = MPMAX(in->opts->back_batch[ds->type], 1);
+ int batch = MPMAX(in->d_user->opts->back_batch[ds->type], 1);
// Number of keyframes to return to the user in total.
int total = batch + ds->back_preroll;
@@ -1507,7 +1488,7 @@ static void find_backward_restart_pos(struct demux_stream *ds)
// Or if preroll is involved, the first preroll packet.
while (ds->reader_head != target) {
if (!advance_reader_head(ds))
- assert(0); // target must be in list
+ MP_ASSERT_UNREACHABLE(); // target must be in list
}
double seek_pts;
@@ -1570,9 +1551,9 @@ resume_earlier:
ds->reader_head = t;
ds->back_need_recheck = true;
in->back_any_need_recheck = true;
- pthread_cond_signal(&in->wakeup);
+ mp_cond_signal(&in->wakeup);
} else {
- ds->back_seek_pos -= in->opts->back_seek_size;
+ ds->back_seek_pos -= in->d_user->opts->back_seek_size;
in->need_back_seek = true;
}
}
@@ -1686,7 +1667,7 @@ static void attempt_range_joining(struct demux_internal *in)
// Try to find a join point, where packets obviously overlap. (It would be
// better and faster to do this incrementally, but probably too complex.)
// The current range can overlap arbitrarily with the next one, not only by
- // by the seek overlap, but for arbitrary packet readahead as well.
+ // the seek overlap, but for arbitrary packet readahead as well.
// We also drop the overlapping packets (if joining fails, we discard the
// entire next range anyway, so this does no harm).
for (int n = 0; n < in->num_streams; n++) {
@@ -1945,9 +1926,18 @@ static struct mp_recorder *recorder_create(struct demux_internal *in,
if (stream->ds->selected)
MP_TARRAY_APPEND(NULL, streams, num_streams, stream);
}
+
+ struct demuxer *demuxer = in->d_thread;
+ struct demux_attachment **attachments = talloc_array(NULL, struct demux_attachment*, demuxer->num_attachments);
+ for (int n = 0; n < demuxer->num_attachments; n++) {
+ attachments[n] = &demuxer->attachments[n];
+ }
+
struct mp_recorder *res = mp_recorder_create(in->d_thread->global, dst,
- streams, num_streams);
+ streams, num_streams,
+ attachments, demuxer->num_attachments);
talloc_free(streams);
+ talloc_free(attachments);
return res;
}
@@ -1970,13 +1960,13 @@ static void record_packet(struct demux_internal *in, struct demux_packet *dp)
{
// (should preferably be outside of the lock)
if (in->enable_recording && !in->recorder &&
- in->opts->record_file && in->opts->record_file[0])
+ in->d_user->opts->record_file && in->d_user->opts->record_file[0])
{
// Later failures shouldn't make it retry and overwrite the previously
// recorded file.
in->enable_recording = false;
- in->recorder = recorder_create(in, in->opts->record_file);
+ in->recorder = recorder_create(in, in->d_user->opts->record_file);
if (!in->recorder)
MP_ERR(in, "Disabling recording.\n");
}
@@ -2000,6 +1990,7 @@ static void record_packet(struct demux_internal *in, struct demux_packet *dp)
static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp)
{
struct demux_stream *ds = stream ? stream->ds : NULL;
+ assert(ds && ds->in);
if (!dp->len || demux_cancel_test(ds->in->d_thread)) {
talloc_free(dp);
return;
@@ -2055,7 +2046,7 @@ static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp)
record_packet(in, dp);
- if (in->cache && in->opts->disk_cache) {
+ if (in->cache && in->d_user->opts->disk_cache) {
int64_t pos = demux_cache_write(in->cache, dp);
if (pos >= 0) {
demux_packet_unref_contents(dp);
@@ -2109,7 +2100,7 @@ static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp)
ds->base_ts = queue->last_ts;
const char *num_pkts = queue->head == queue->tail ? "1" : ">1";
- uint64_t fw_bytes = get_foward_buffered_bytes(ds);
+ uint64_t fw_bytes = get_forward_buffered_bytes(ds);
MP_TRACE(in, "append packet to %s: size=%zu pts=%f dts=%f pos=%"PRIi64" "
"[num=%s size=%zd]\n", stream_type_name(stream->type),
dp->len, dp->pts, dp->dts, dp->pos, num_pkts, (size_t)fw_bytes);
@@ -2160,7 +2151,7 @@ static bool lazy_stream_needs_wait(struct demux_stream *ds)
struct demux_internal *in = ds->in;
// Attempt to read until force_read_until was reached, or reading has
// stopped for some reason (true EOF, queue overflow).
- return !ds->eager && !ds->reader_head && !in->back_demuxing &&
+ return !ds->eager && !in->back_demuxing &&
!in->eof && ds->force_read_until != MP_NOPTS_VALUE &&
(in->demux_ts == MP_NOPTS_VALUE ||
in->demux_ts <= ds->force_read_until);
@@ -2198,16 +2189,23 @@ static bool read_packet(struct demux_internal *in)
in->min_secs > 0 && ds->base_ts != MP_NOPTS_VALUE &&
ds->queue->last_ts >= ds->base_ts &&
!in->back_demuxing)
- prefetch_more |= ds->queue->last_ts - ds->base_ts < in->min_secs;
- total_fw_bytes += get_foward_buffered_bytes(ds);
+ {
+ if (ds->queue->last_ts - ds->base_ts <= in->hyst_secs)
+ in->hyst_active = false;
+ if (!in->hyst_active)
+ prefetch_more |= ds->queue->last_ts - ds->base_ts < in->min_secs;
+ }
+ total_fw_bytes += get_forward_buffered_bytes(ds);
}
MP_TRACE(in, "bytes=%zd, read_more=%d prefetch_more=%d, refresh_more=%d\n",
(size_t)total_fw_bytes, read_more, prefetch_more, refresh_more);
if (total_fw_bytes >= in->max_bytes) {
// if we hit the limit just by prefetching, simply stop prefetching
- if (!read_more)
+ if (!read_more) {
+ in->hyst_active = !!in->hyst_secs;
return false;
+ }
if (!in->warned_queue_overflow) {
in->warned_queue_overflow = true;
MP_WARN(in, "Too many packets in the demuxer packet queues:\n");
@@ -2218,7 +2216,7 @@ static bool read_packet(struct demux_internal *in)
for (struct demux_packet *dp = ds->reader_head;
dp; dp = dp->next)
num_pkts++;
- uint64_t fw_bytes = get_foward_buffered_bytes(ds);
+ uint64_t fw_bytes = get_forward_buffered_bytes(ds);
MP_WARN(in, " %s/%d: %zd packets, %zd bytes%s%s\n",
stream_type_name(ds->type), n,
num_pkts, (size_t)fw_bytes,
@@ -2237,8 +2235,10 @@ static bool read_packet(struct demux_internal *in)
return false;
}
- if (!read_more && !prefetch_more && !refresh_more)
+ if (!read_more && !prefetch_more && !refresh_more) {
+ in->hyst_active = !!in->hyst_secs;
return false;
+ }
if (in->after_seek_to_start) {
for (int n = 0; n < in->num_streams; n++) {
@@ -2253,7 +2253,7 @@ static bool read_packet(struct demux_internal *in)
in->reading = true;
in->after_seek = false;
in->after_seek_to_start = false;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
struct demuxer *demux = in->d_thread;
struct demux_packet *pkt = NULL;
@@ -2262,7 +2262,7 @@ static bool read_packet(struct demux_internal *in)
if (demux->desc->read_packet && !demux_cancel_test(demux))
eof = !demux->desc->read_packet(demux, &pkt);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
update_cache(in);
if (pkt) {
@@ -2278,7 +2278,7 @@ static bool read_packet(struct demux_internal *in)
if (!in->eof) {
if (in->wakeup_cb)
in->wakeup_cb(in->wakeup_cb_ctx);
- pthread_cond_signal(&in->wakeup);
+ mp_cond_signal(&in->wakeup);
MP_VERBOSE(in, "EOF reached.\n");
}
}
@@ -2299,12 +2299,12 @@ static void prune_old_packets(struct demux_internal *in)
uint64_t fw_bytes = 0;
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *ds = in->streams[n]->ds;
- fw_bytes += get_foward_buffered_bytes(ds);
+ fw_bytes += get_forward_buffered_bytes(ds);
}
uint64_t max_avail = in->max_bytes_bw;
// Backward cache (if enabled at all) can use unused forward cache.
// Still leave 1 byte free, so the read_packet logic doesn't get stuck.
- if (max_avail && in->max_bytes > (fw_bytes + 1) && in->opts->donate_fw)
+ if (max_avail && in->max_bytes > (fw_bytes + 1) && in->d_user->opts->donate_fw)
max_avail += in->max_bytes - (fw_bytes + 1);
if (in->total_bytes - fw_bytes <= max_avail)
break;
@@ -2407,16 +2407,12 @@ static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
- bool any_selected = false;
- for (int n = 0; n < in->num_streams; n++)
- any_selected |= in->streams[n]->ds->selected;
-
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
if (in->d_thread->desc->switched_tracks)
in->d_thread->desc->switched_tracks(in->d_thread);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
}
static void execute_seek(struct demux_internal *in)
@@ -2439,7 +2435,7 @@ static void execute_seek(struct demux_internal *in)
if (in->recorder)
mp_recorder_mark_discontinuity(in->recorder);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
MP_VERBOSE(in, "execute seek (to %f flags %d)\n", pts, flags);
@@ -2448,16 +2444,18 @@ static void execute_seek(struct demux_internal *in)
MP_VERBOSE(in, "seek done\n");
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->seeking_in_progress = MP_NOPTS_VALUE;
}
-static void update_opts(struct demux_internal *in)
+static void update_opts(struct demuxer *demuxer)
{
- struct demux_opts *opts = in->opts;
+ struct demux_opts *opts = demuxer->opts;
+ struct demux_internal *in = demuxer->in;
in->min_secs = opts->min_secs;
+ in->hyst_secs = opts->hyst_secs;
in->max_bytes = opts->max_bytes;
in->max_bytes_bw = opts->max_bytes_bw;
@@ -2520,8 +2518,8 @@ static void update_opts(struct demux_internal *in)
// Make demuxing progress. Return whether progress was made.
static bool thread_work(struct demux_internal *in)
{
- if (m_config_cache_update(in->opts_cache))
- update_opts(in);
+ if (m_config_cache_update(in->d_user->opts_cache))
+ update_opts(in->d_user);
if (in->tracks_switched) {
execute_trackswitch(in);
return true;
@@ -2540,33 +2538,32 @@ static bool thread_work(struct demux_internal *in)
}
if (read_packet(in))
return true; // read_packet unlocked, so recheck conditions
- if (mp_time_us() >= in->next_cache_update) {
+ if (mp_time_ns() >= in->next_cache_update) {
update_cache(in);
return true;
}
return false;
}
-static void *demux_thread(void *pctx)
+static MP_THREAD_VOID demux_thread(void *pctx)
{
struct demux_internal *in = pctx;
- mpthread_set_name("demux");
- pthread_mutex_lock(&in->lock);
+ mp_thread_set_name("demux");
+ mp_mutex_lock(&in->lock);
stats_register_thread_cputime(in->stats, "thread");
while (!in->thread_terminate) {
if (thread_work(in))
continue;
- pthread_cond_signal(&in->wakeup);
- struct timespec until = mp_time_us_to_timespec(in->next_cache_update);
- pthread_cond_timedwait(&in->wakeup, &in->lock, &until);
+ mp_cond_signal(&in->wakeup);
+ mp_cond_timedwait_until(&in->wakeup, &in->lock, in->next_cache_update);
}
if (in->shutdown_async) {
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
demux_shutdown(in);
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
in->shutdown_async = false;
if (in->wakeup_cb)
in->wakeup_cb(in->wakeup_cb_ctx);
@@ -2574,8 +2571,8 @@ static void *demux_thread(void *pctx)
stats_unregister_thread(in->stats, "thread");
- pthread_mutex_unlock(&in->lock);
- return NULL;
+ mp_mutex_unlock(&in->lock);
+ MP_THREAD_RETURN();
}
// Low-level part of dequeueing a packet.
@@ -2639,16 +2636,15 @@ static int dequeue_packet(struct demux_stream *ds, double min_pts,
return -1;
ds->attached_picture_added = true;
struct demux_packet *pkt = demux_copy_packet(ds->sh->attached_picture);
- if (!pkt)
- abort();
+ MP_HANDLE_OOM(pkt);
pkt->stream = ds->sh->index;
*res = pkt;
return 1;
}
- if (!in->reading && (!in->eof || in->opts->force_retry_eof)) {
+ if (!in->reading && !in->eof) {
in->reading = true; // enable demuxer thread prefetching
- pthread_cond_signal(&in->wakeup);
+ mp_cond_signal(&in->wakeup);
}
ds->force_read_until = min_pts;
@@ -2787,7 +2783,7 @@ int demux_read_packet_async_until(struct sh_stream *sh, double min_pts,
return -1;
struct demux_internal *in = ds->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
int r = -1;
while (1) {
r = dequeue_packet(ds, min_pts, out_pkt);
@@ -2796,7 +2792,7 @@ int demux_read_packet_async_until(struct sh_stream *sh, double min_pts,
// Needs to actually read packets until we got a packet or EOF.
thread_work(in);
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return r;
}
@@ -2805,7 +2801,7 @@ int demux_read_packet_async_until(struct sh_stream *sh, double min_pts,
struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
{
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
assert(!in->threading); // doesn't work with threading
struct demux_packet *out_pkt = NULL;
bool read_more = true;
@@ -2823,11 +2819,11 @@ struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
read_more &= !all_eof;
}
done:
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
return out_pkt;
}
-void demuxer_help(struct mp_log *log)
+int demuxer_help(struct mp_log *log, const m_option_t *opt, struct bstr name)
{
int i;
@@ -2837,6 +2833,9 @@ void demuxer_help(struct mp_log *log)
mp_info(log, "%10s %s\n",
demuxer_list[i]->name, demuxer_list[i]->desc);
}
+ mp_info(log, "\n");
+
+ return M_OPT_EXIT;
}
static const char *d_level(enum demux_check level)
@@ -2847,7 +2846,7 @@ static const char *d_level(enum demux_check level)
case DEMUX_CHECK_REQUEST:return "request";
case DEMUX_CHECK_NORMAL: return "normal";
}
- abort();
+ MP_ASSERT_UNREACHABLE();
}
static int decode_float(char *str, float *out)
@@ -2931,6 +2930,24 @@ static struct replaygain_data *decode_rgain(struct mp_log *log,
return talloc_dup(NULL, &rg);
}
+ // The r128 replaygain tags declared in RFC 7845 for opus files. The tags
+ // are generated with EBU-R128, which does not use peak meters. And the
+ // values are stored as a Q7.8 fixed point number in dB.
+ if (decode_gain(log, tags, "R128_TRACK_GAIN", &rg.track_gain) >= 0) {
+ if (decode_gain(log, tags, "R128_ALBUM_GAIN", &rg.album_gain) < 0) {
+ // Album gain is undefined; fall back to track gain.
+ rg.album_gain = rg.track_gain;
+ }
+ rg.track_gain /= 256.;
+ rg.album_gain /= 256.;
+
+ // Add 5dB to compensate for the different reference levels between
+ // our reference of ReplayGain 2 (-18 LUFS) and EBU R128 (-23 LUFS).
+ rg.track_gain += 5.;
+ rg.album_gain += 5.;
+ return talloc_dup(NULL, &rg);
+ }
+
return NULL;
}
@@ -3025,7 +3042,7 @@ void demux_stream_tags_changed(struct demuxer *demuxer, struct sh_stream *sh,
struct demux_stream *ds = sh ? sh->ds : NULL;
assert(!sh || ds); // stream must have been added
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (pts == MP_NOPTS_VALUE) {
MP_WARN(in, "Discarding timed metadata without timestamp.\n");
@@ -3034,7 +3051,7 @@ void demux_stream_tags_changed(struct demuxer *demuxer, struct sh_stream *sh,
}
talloc_free(tags);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
// This is called by demuxer implementations if demuxer->metadata changed.
@@ -3044,9 +3061,9 @@ void demux_metadata_changed(demuxer_t *demuxer)
assert(demuxer == demuxer->in->d_thread); // call from demuxer impl. only
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
add_timed_metadata(in, demuxer->metadata, NULL, MP_NOPTS_VALUE);
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
// Called locked, with user demuxer.
@@ -3124,7 +3141,7 @@ void demux_update(demuxer_t *demuxer, double pts)
assert(demuxer == demuxer->in->d_user);
struct demux_internal *in = demuxer->in;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
if (!in->threading)
update_cache(in);
@@ -3151,24 +3168,31 @@ void demux_update(demuxer_t *demuxer, double pts)
if (demuxer->events & DEMUX_EVENT_DURATION)
demuxer->duration = in->duration;
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}
static void demux_init_cuesheet(struct demuxer *demuxer)
{
+ if (demuxer->num_chapters)
+ return;
+
+ struct sh_stream *sh = demuxer->in->metadata_stream;
char *cue = mp_tags_get_str(demuxer->metadata, "cuesheet");
- if (cue && !demuxer->num_chapters) {
- struct cue_file *f = mp_parse_cue(bstr0(cue));
- if (f) {
- if (mp_check_embedded_cue(f) < 0) {
- MP_WARN(demuxer, "Embedded cue sheet references more than one file. "
- "Ignoring it.\n");
- } else {
- for (int n = 0; n < f->num_tracks; n++) {
- struct cue_track *t = &f->tracks[n];
- int idx = demuxer_add_chapter(demuxer, "", t->start, -1);
- mp_tags_merge(demuxer->chapters[idx].metadata, t->tags);
- }
+ if (!cue && sh)
+ cue = mp_tags_get_str(sh->tags, "cuesheet");
+ if (!cue)
+ return;
+
+ struct cue_file *f = mp_parse_cue(bstr0(cue));
+ if (f) {
+ if (mp_check_embedded_cue(f) < 0) {
+ MP_WARN(demuxer, "Embedded cue sheet references more than one file. "
+ "Ignoring it.\n");
+ } else {
+ for (int n = 0; n < f->num_tracks; n++) {
+ struct cue_track *t = &f->tracks[n];
+ int idx = demuxer_add_chapter(demuxer, "", t->start, -1);
+ mp_tags_merge(demuxer->chapters[idx].metadata, t->tags);
}
}
talloc_free(f);
@@ -3200,13 +3224,13 @@ static void demux_init_ccs(struct demuxer *demuxer, struct demux_opts *opts)
struct demux_internal *in = demuxer->in;
if (!opts->create_ccs)
return;
- pthread_mutex_lock(&in->lock);
+ mp_mutex_lock(&in->lock);
for (int n = 0; n < in->num_streams; n++) {
struct sh_stream *sh = in->streams[n];
if (sh->type == STREAM_VIDEO && !sh->attached_picture)
demuxer_get_cc_track_locked(sh);
}
- pthread_mutex_unlock(&in->lock);
+ mp_mutex_unlock(&in->lock);
}