/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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->type == STREAM_VIDEO) {
if (s && s->codec->disp_w)
APPEND(b, " %dx%d", s->codec->disp_w, s->codec->disp_h);
if (s && s->codec->fps)
APPEND(b, " %.3ffps", s->codec->fps);
} else if (t->type == STREAM_AUDIO) {
if (s && s->codec->channels.num)
APPEND(b, " %dch", s->codec->channels.num);
if (s && s->codec->samplerate)
APPEND(b, " %dHz", s->codec->samplerate);
}
APPEND(b, ")");
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];
|