/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <stdbool.h>
#include <strings.h>
#include <inttypes.h>
#include <assert.h>
#include <libavutil/avutil.h>
#include "config.h"
#include "mpv_talloc.h"
#include "osdep/io.h"
#include "osdep/terminal.h"
#include "osdep/timer.h"
#include "common/msg.h"
#include "common/global.h"
#include "options/path.h"
#include "options/m_config.h"
#include "options/parse_configfile.h"
#include "common/playlist.h"
#include "options/options.h"
#include "options/m_property.h"
#include "common/common.h"
#include "common/encode.h"
#include "common/recorder.h"
#include "input/input.h"
#include "audio/audio.h"
#include "audio/audio_buffer.h"
#include "audio/decode/dec_audio.h"
#include "audio/out/ao.h"
#include "demux/demux.h"
#include "stream/stream.h"
#include "sub/dec_sub.h"
#include "external_files.h"
#include "video/decode/dec_video.h"
#include "video/out/vo.h"
#include "core.h"
#include "command.h"
#include "libmpv/client.h"
// Called by foreign threads when playback should be stopped and such.
void mp_abort_playback_async(struct MPContext *mpctx)
{
mp_cancel_trigger(mpctx->playback_abort);
pthread_mutex_lock(&mpctx->lock);
if (mpctx->demuxer_cancel)
mp_cancel_trigger(mpctx->demuxer_cancel);
pthread_mutex_unlock(&mpctx->lock);
}
static void uninit_demuxer(struct MPContext *mpctx)
{
for (int r = 0; r < NUM_PTRACKS; r++) {
for (int t = 0; t < STREAM_TYPE_COUNT; t++)
mpctx->current_track[r][t] = NULL;
}
talloc_free(mpctx->chapters);
mpctx->chapters = NULL;
mpctx->num_chapters = 0;
// close demuxers for external tracks
for (int n = mpctx->num_tracks - 1; n >= 0; n--) {
mpctx->tracks[n]->selected = false;
mp_remove_track(mpctx, mpctx->tracks[n]);
}
for (int i = 0; i < mpctx->num_tracks; i++) {
sub_destroy(mpctx->tracks[i]->d_sub);
talloc_free(mpctx->tracks[i]);
}
mpctx->num_tracks = 0;
free_demuxer_and_stream(mpctx->demuxer);
mpctx->demuxer = NULL;
pthread_mutex_lock(&mpctx->lock);
talloc_free(mpctx->demuxer_cancel);
mpctx->demuxer_cancel = NULL;
pthread_mutex_unlock(&mpctx->lock);
}
#define APPEND(s, ...) mp_snprintf_cat(s, sizeof(s), __VA_ARGS__)
static void print_stream(struct MPContext *mpctx, struct track *t)
{
struct sh_stream *s = t->stream;
const char *tname = "?";
const char *selopt = "?";
const char *langopt = "?";
switch (t->type) {
case STREAM_VIDEO:
tname = "Video"; selopt = "vid"; langopt = NULL;
break;
case STREAM_AUDIO:
tname = "Audio"; selopt = "aid"; langopt = "alang";
break;
case STREAM_SUB:
tname = "Subs"; selopt = "sid"; langopt = "slang";
break;
}
char b[2048] = {0};
APPEND(b, " %3s %-5s", t->selected ? "(+)" : "", tname);
APPEND(b, " --%s=%d", selopt, t->user_tid);
if (t->lang && langopt)
APPEND(b, " --%s=%s", langopt, t->lang);
if (t->default_track)
APPEND(b, " (*)");
if (t->forced_track)
APPEND(b, " (f)");
if (t->attached_picture)
APPEND(b, " [P]");
if (t->title)
APPEND(b, " '%s'", t->title);
const char *codec = s ? s->codec->codec : NULL;
APPEND(b, " (%s)", codec ? codec : "<unknown>");
if (t->is_external)
APPEND(b, " (external)");
MP_INFO(mpctx, "%s\n", b);
}
void print_track_list(struct MPContext *mpctx, const char *msg)
{
if (msg)
MP_INFO(mpctx, "%s\n", msg);
for (int t = 0; t < STREAM_TYPE_COUNT; t++) {
for (int n = 0; n < mpctx->num_tracks; n++)
if (mpctx->tracks[n]->type == t)
print_stream(mpctx, mpctx->tracks[n]);
}
}
void update_demuxer_properties(struct MPContext *mpctx)
{
struct demuxer *demuxer = mpctx->demuxer;
if (!demuxer)
return;
demux_update(demuxer);
int events = demuxer->events;
if ((events & DEMUX_EVENT_INIT) && demuxer->num_editions > 1) {
for (int n = 0; n < demuxer->num_editions; n++) {
struct demux_edition *edition = &demuxer->editions[n];
char b[128] = {0};
APPEND(b, " %3s --edition=%d",
n == demuxer->edition ? "(+)" : "", n);
char *name = mp_tags_get_str(edition->metadata, "title");
if (name)
APPEND(b, " '%s'", name);
if (edition->default_edition)
APPEND(b, " (*)");
MP_INFO(mpctx, "%s\n", b);
}
}
struct demuxer *tracks = mpctx->demuxer;
if (tracks->events & DEMUX_EVENT_STREAMS) {
add_demuxer_tracks(mpctx, tracks);
print_track_list(mpctx, NULL);
tracks->events &= ~DEMUX_EVENT_STREAMS;
}
if (events & DEMUX_EVENT_METADATA) {
struct mp_tags *info =
mp_tags_filtered(mpctx, demuxer->metadata, mpctx->opts->display_tags);
// prev is used to attempt to print changed tags only (to some degree)
struct mp_tags *prev = mpctx->filtered_tags;
int n_prev = 0;
bool had_output = false;
for (int n = 0; n < info->num_keys; n++) {
if (prev && n_prev < prev->num_keys) {
if (strcmp(prev->keys[n_prev], info->keys[n]) == 0) {
n_prev++;
if (strcmp(prev->values[n_prev - 1], info->values[n]) == 0)
continue;
}
}
|