summaryrefslogtreecommitdiffstats
path: root/demux/demux_lavf.c
diff options
context:
space:
mode:
Diffstat (limited to 'demux/demux_lavf.c')
-rw-r--r--demux/demux_lavf.c189
1 files changed, 107 insertions, 82 deletions
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 2d12f5cfa2..1b3105170d 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -40,6 +40,7 @@
#include "common/tags.h"
#include "common/av_common.h"
#include "misc/bstr.h"
+#include "misc/charset_conv.h"
#include "stream/stream.h"
#include "demux.h"
@@ -108,6 +109,7 @@ struct format_hack {
bool no_stream : 1; // do not wrap struct stream as AVIOContext
bool use_stream_ids : 1; // export the native stream IDs
bool fully_read : 1; // set demuxer.fully_read flag
+ bool detect_charset : 1; // format is a small text file, possibly not UTF8
bool image_format : 1; // expected to contain exactly 1 frame
// Do not confuse player's position estimation (position is into external
// segment, with e.g. HLS, player knows about the playlist main file only).
@@ -115,8 +117,8 @@ struct format_hack {
};
#define BLACKLIST(fmt) {fmt, .ignore = true}
-#define TEXTSUB(fmt) {fmt, .fully_read = true}
-#define IMAGEFMT(fmt) {fmt, .image_format = true}
+#define TEXTSUB(fmt) {fmt, .fully_read = true, .detect_charset = true}
+#define TEXTSUB_UTF8(fmt) {fmt, .fully_read = true}
static const struct format_hack format_hacks[] = {
// for webradios
@@ -135,10 +137,12 @@ static const struct format_hack format_hacks[] = {
{"h264", .if_flags = AVFMT_NOTIMESTAMPS },
{"hevc", .if_flags = AVFMT_NOTIMESTAMPS },
- TEXTSUB("aqtitle"), TEXTSUB("ass"), TEXTSUB("jacosub"), TEXTSUB("microdvd"),
+ TEXTSUB("aqtitle"), TEXTSUB("jacosub"), TEXTSUB("microdvd"),
TEXTSUB("mpl2"), TEXTSUB("mpsub"), TEXTSUB("pjs"), TEXTSUB("realtext"),
TEXTSUB("sami"), TEXTSUB("srt"), TEXTSUB("stl"), TEXTSUB("subviewer"),
- TEXTSUB("subviewer1"), TEXTSUB("vplayer"), TEXTSUB("webvtt"),
+ TEXTSUB("subviewer1"), TEXTSUB("vplayer"), TEXTSUB("ass"),
+
+ TEXTSUB_UTF8("webvtt"),
// Useless non-sense, sometimes breaks MLP2 subreader.c fallback
BLACKLIST("tty"),
@@ -148,11 +152,12 @@ static const struct format_hack format_hacks[] = {
// Useless, does not work with custom streams.
BLACKLIST("image2"),
// Image demuxers ("<name>_pipe" is detected explicitly)
- IMAGEFMT("image2pipe"),
+ {"image2pipe", .image_format = true},
{0}
};
typedef struct lavf_priv {
+ struct stream *stream;
char *filename;
struct format_hack format_hack;
AVInputFormat *avif;
@@ -187,7 +192,8 @@ static bool matches_avinputformat_name(struct lavf_priv *priv,
static int mp_read(void *opaque, uint8_t *buf, int size)
{
struct demuxer *demuxer = opaque;
- struct stream *stream = demuxer->stream;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *stream = priv->stream;
int ret;
ret = stream_read(stream, buf, size);
@@ -200,9 +206,13 @@ static int mp_read(void *opaque, uint8_t *buf, int size)
static int64_t mp_seek(void *opaque, int64_t pos, int whence)
{
struct demuxer *demuxer = opaque;
- struct stream *stream = demuxer->stream;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *stream = priv->stream;
int64_t current_pos;
- MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %d)\n", stream, pos, whence);
+ MP_TRACE(demuxer, "mp_seek(%p, %"PRId64", %s)\n", stream, pos,
+ whence == SEEK_END ? "end" :
+ whence == SEEK_CUR ? "cur" :
+ whence == SEEK_SET ? "set" : "size");
if (whence == SEEK_END || whence == AVSEEK_SIZE) {
int64_t end = stream_get_size(stream);
if (end < 0)
@@ -230,7 +240,8 @@ static int64_t mp_seek(void *opaque, int64_t pos, int whence)
static int64_t mp_read_seek(void *opaque, int stream_idx, int64_t ts, int flags)
{
struct demuxer *demuxer = opaque;
- struct stream *stream = demuxer->stream;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *stream = priv->stream;
struct stream_avseek cmd = {
.stream_index = stream_idx,
@@ -253,6 +264,32 @@ static void list_formats(struct demuxer *demuxer)
MP_INFO(demuxer, "%15s : %s\n", fmt->name, fmt->long_name);
}
+static void convert_charset(struct demuxer *demuxer)
+{
+ lavf_priv_t *priv = demuxer->priv;
+ char *cp = demuxer->opts->sub_cp;
+ if (!cp || mp_charset_is_utf8(cp))
+ return;
+ bstr data = stream_read_complete(priv->stream, NULL, 128 * 1024 * 1024);
+ if (!data.start) {
+ MP_WARN(demuxer, "File too big (or error reading) - skip charset probing.\n");
+ return;
+ }
+ void *alloc = data.start;
+ cp = (char *)mp_charset_guess(priv, demuxer->log, data, cp, 0);
+ if (cp && !mp_charset_is_utf8(cp))
+ MP_INFO(demuxer, "Using subtitle charset: %s\n", cp);
+ // libavformat transparently converts UTF-16 to UTF-8
+ if (!mp_charset_is_utf16(cp) && !mp_charset_is_utf8(cp)) {
+ bstr conv = mp_iconv_to_utf8(demuxer->log, data, cp, MP_ICONV_VERBOSE);
+ if (conv.start)
+ data = conv;
+ }
+ if (data.start)
+ priv->stream = open_memory_stream(data.start, data.len);
+ talloc_free(alloc);
+}
+
static char *remove_prefix(char *s, const char *const *prefixes)
{
for (int n = 0; prefixes[n]; n++) {
@@ -270,12 +307,8 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
{
struct MPOpts *opts = demuxer->opts;
struct demux_lavf_opts *lavfdopts = opts->demux_lavf;
- struct stream *s = demuxer->stream;
- lavf_priv_t *priv;
-
- assert(!demuxer->priv);
- demuxer->priv = talloc_zero(NULL, lavf_priv_t);
- priv = demuxer->priv;
+ lavf_priv_t *priv = demuxer->priv;
+ struct stream *s = priv->stream;
priv->filename = remove_prefix(s->url, prefixes);
@@ -292,7 +325,7 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
priv->filename = sep + 1;
}
- char *mime_type = demuxer->stream->mime_type;
+ char *mime_type = s->mime_type;
if (!lavfdopts->allow_mimetype || !mime_type)
mime_type = "";
@@ -393,6 +426,9 @@ static int lavf_check_file(demuxer_t *demuxer, enum demux_check check)
demuxer->filetype = priv->avif->name;
+ if (priv->format_hack.detect_charset)
+ convert_charset(demuxer);
+
return 0;
}
@@ -464,7 +500,8 @@ static void select_tracks(struct demuxer *demuxer, int start)
}
}
-static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
+static void export_replaygain(demuxer_t *demuxer, struct mp_codec_params *c,
+ AVStream *st)
{
for (int i = 0; i < st->nb_side_data; i++) {
AVReplayGain *av_rgain;
@@ -489,7 +526,7 @@ static void export_replaygain(demuxer_t *demuxer, sh_audio_t *sh, AVStream *st)
rgain->album_peak = (av_rgain->album_peak != 0.0) ?
av_rgain->album_peak / 100000.0f : 1.0;
- sh->replaygain_data = rgain;
+ c->replaygain_data = rgain;
}
}
@@ -506,7 +543,7 @@ static int dict_get_decimal(AVDictionary *dict, const char *entry, int def)
return def;
}
-static void handle_stream(demuxer_t *demuxer, int i)
+static void handle_new_stream(demuxer_t *demuxer, int i)
{
lavf_priv_t *priv = demuxer->priv;
AVFormatContext *avfc = priv->avfc;
@@ -516,27 +553,21 @@ static void handle_stream(demuxer_t *demuxer, int i)
switch (codec->codec_type) {
case AVMEDIA_TYPE_AUDIO: {
- sh = new_sh_stream(demuxer, STREAM_AUDIO);
- if (!sh)
- break;
- sh_audio_t *sh_audio = sh->audio;
+ sh = demux_alloc_sh_stream(STREAM_AUDIO);
// probably unneeded
- mp_chmap_set_unknown(&sh_audio->channels, codec->channels);
+ mp_chmap_set_unknown(&sh->codec->channels, codec->channels);
if (codec->channel_layout)
- mp_chmap_from_lavc(&sh_audio->channels, codec->channel_layout);
- sh_audio->samplerate = codec->sample_rate;
- sh_audio->bitrate = codec->bit_rate;
+ mp_chmap_from_lavc(&sh->codec->channels, codec->channel_layout);
+ sh->codec->samplerate = codec->sample_rate;
+ sh->codec->bitrate = codec->bit_rate;
- export_replaygain(demuxer, sh_audio, st);
+ export_replaygain(demuxer, sh->codec, st);
break;
}
case AVMEDIA_TYPE_VIDEO: {
- sh = new_sh_stream(demuxer, STREAM_VIDEO);
- if (!sh)
- break;
- sh_video_t *sh_video = sh->video;
+ sh = demux_alloc_sh_stream(STREAM_VIDEO);
if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) {
sh->attached_picture = new_demux_packet_from(st->attached_pic.data,
@@ -548,8 +579,8 @@ static void handle_stream(demuxer_t *demuxer, int i)
}
}
- sh_video->disp_w = codec->width;
- sh_video->disp_h = codec->height;
+ sh->codec->disp_w = codec->width;
+ sh->codec->disp_h = codec->height;
/* Try to make up some frame rate value, even if it's not reliable.
* FPS information is needed to support subtitle formats which base
* timing on frame numbers.
@@ -564,39 +595,31 @@ static void handle_stream(demuxer_t *demuxer, int i)
fps = 1.0 / FFMAX(av_q2d(st->time_base),
av_q2d(st->codec->time_base) *
st->codec->ticks_per_frame);
- sh_video->fps = fps;
+ sh->codec->fps = fps;
if (priv->format_hack.image_format)
- sh_video->fps = demuxer->opts->mf_fps;
- if (st->sample_aspect_ratio.num)
- sh_video->aspect = codec->width * st->sample_aspect_ratio.num
- / (float)(codec->height * st->sample_aspect_ratio.den);
- else
- sh_video->aspect = codec->width * codec->sample_aspect_ratio.num
- / (float)(codec->height * codec->sample_aspect_ratio.den);
+ sh->codec->fps = demuxer->opts->mf_fps;
+ sh->codec->par_w = st->sample_aspect_ratio.num;
+ sh->codec->par_h = st->sample_aspect_ratio.den;
uint8_t *sd = av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (sd) {
double r = av_display_rotation_get((uint32_t *)sd);
if (!isnan(r))
- sh_video->rotate = (((int)(-r) % 360) + 360) % 360;
+ sh->codec->rotate = (((int)(-r) % 360) + 360) % 360;
}
// This also applies to vfw-muxed mkv, but we can't detect these easily.
- sh_video->avi_dts = matches_avinputformat_name(priv, "avi");
+ sh->codec->avi_dts = matches_avinputformat_name(priv, "avi");
break;
}
case AVMEDIA_TYPE_SUBTITLE: {
- sh_sub_t *sh_sub;
- sh = new_sh_stream(demuxer, STREAM_SUB);
- if (!sh)
- break;
- sh_sub = sh->sub;
+ sh = demux_alloc_sh_stream(STREAM_SUB);
if (codec->extradata_size) {
- sh->extradata = talloc_size(sh, codec->extradata_size);
- memcpy(sh->extradata, codec->extradata, codec->extradata_size);
- sh->extradata_size = codec->extradata_size;
+ sh->codec->extradata = talloc_size(sh, codec->extradata_size);
+ memcpy(sh->codec->extradata, codec->extradata, codec->extradata_size);
+ sh->codec->extradata_size = codec->extradata_size;
}
if (matches_avinputformat_name(priv, "microdvd")) {
@@ -604,18 +627,14 @@ static void handle_stream(demuxer_t *demuxer, int i)
if (av_opt_get_q(avfc, "subfps", AV_OPT_SEARCH_CHILDREN, &r) >= 0) {
// File headers don't have a FPS set.
if (r.num < 1 || r.den < 1)
- sh_sub->frame_based = av_q2d(av_inv_q(codec->time_base));
+ sh->codec->frame_based = av_q2d(av_inv_q(codec->time_base));
} else {
// Older libavformat versions. If the FPS matches the microdvd
// reader's default, assume it uses frame based timing.
if (codec->time_base.num == 125 && codec->time_base.den == 2997)
- sh_sub->frame_based = 23.976;
+ sh->codec->frame_based = 23.976;
}
}
-
- if (matches_avinputformat_name(priv, "ass"))
- sh_sub->is_utf8 = true;
-
break;
}
case AVMEDIA_TYPE_ATTACHMENT: {
@@ -637,12 +656,11 @@ static void handle_stream(demuxer_t *demuxer, int i)
if (sh) {
sh->ff_index = st->index;
- sh->codec = mp_codec_from_av_codec_id(codec->codec_id);
- sh->codec_tag = codec->codec_tag;
- sh->lav_headers = avcodec_alloc_context3(NULL);
- if (!sh->lav_headers)
- return;
- mp_copy_lav_codec_headers(sh->lav_headers, codec);
+ sh->codec->codec = mp_codec_from_av_codec_id(codec->codec_id);
+ sh->codec->codec_tag = codec->codec_tag;
+ sh->codec->lav_headers = avcodec_alloc_context3(NULL);
+ if (sh->codec->lav_headers)
+ mp_copy_lav_codec_headers(sh->codec->lav_headers, codec);
if (st->disposition & AV_DISPOSITION_DEFAULT)
sh->default_track = true;
@@ -660,10 +678,10 @@ static void handle_stream(demuxer_t *demuxer, int i)
if (!sh->title && sh->hls_bitrate > 0)
sh->title = talloc_asprintf(sh, "bitrate %d", sh->hls_bitrate);
sh->missing_timestamps = !!(priv->avif_flags & AVFMT_NOTIMESTAMPS);
+ demux_add_sh_stream(demuxer, sh);
}
select_tracks(demuxer, i);
- demux_changed(demuxer, DEMUX_EVENT_STREAMS);
}
// Add any new streams that might have been added
@@ -671,7 +689,7 @@ static void add_new_streams(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
while (priv->num_streams < priv->avfc->nb_streams)
- handle_stream(demuxer, priv->num_streams);
+ handle_new_stream(demuxer, priv->num_streams);
}
static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
@@ -697,7 +715,8 @@ static void update_metadata(demuxer_t *demuxer, AVPacket *pkt)
static int interrupt_cb(void *ctx)
{
struct demuxer *demuxer = ctx;
- return mp_cancel_test(demuxer->stream->cancel);
+ lavf_priv_t *priv = demuxer->priv;
+ return mp_cancel_test(priv->stream->cancel);
}
static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
@@ -707,15 +726,13 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
AVFormatContext *avfc;
AVDictionaryEntry *t = NULL;
float analyze_duration = 0;
- int i;
+ lavf_priv_t *priv = talloc_zero(NULL, lavf_priv_t);
+ demuxer->priv = priv;
+ priv->stream = demuxer->stream;
if (lavf_check_file(demuxer, check) < 0)
return -1;
- lavf_priv_t *priv = demuxer->priv;
- if (!priv)
- return -1;
-
avfc = avformat_alloc_context();
if (!avfc)
return -1;
@@ -754,7 +771,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
AVDictionary *dopts = NULL;
if ((priv->avif_flags & AVFMT_NOFILE) ||
- demuxer->stream->type == STREAMTYPE_AVDEVICE ||
+ priv->stream->type == STREAMTYPE_AVDEVICE ||
priv->format_hack.no_stream)
{
mp_setup_av_network_options(&dopts, demuxer->global, demuxer->log, opts);
@@ -773,7 +790,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
priv->pb->read_seek = mp_read_seek;
priv->pb->seekable = demuxer->seekable ? AVIO_SEEKABLE_NORMAL : 0;
avfc->pb = priv->pb;
- if (stream_control(demuxer->stream, STREAM_CTRL_HAS_AVSEEK, NULL) > 0)
+ if (stream_control(priv->stream, STREAM_CTRL_HAS_AVSEEK, NULL) > 0)
demuxer->seekable = true;
}
@@ -813,9 +830,9 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
}
MP_VERBOSE(demuxer, "avformat_find_stream_info() finished after %"PRId64
- " bytes.\n", stream_tell(demuxer->stream));
+ " bytes.\n", stream_tell(priv->stream));
- for (i = 0; i < avfc->nb_chapters; i++) {
+ for (int i = 0; i < avfc->nb_chapters; i++) {
AVChapter *c = avfc->chapters[i];
t = av_dict_get(c->metadata, "title", NULL, 0);
int index = demuxer_add_chapter(demuxer, t ? t->value : "",
@@ -827,7 +844,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
// Often useful with OGG audio-only files, which have metadata in the audio
// track metadata instead of the main metadata.
- if (demuxer->num_streams == 1) {
+ if (demux_get_num_stream(demuxer) == 1) {
priv->merge_track_metadata = true;
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n])
@@ -925,7 +942,7 @@ static void demux_seek_lavf(demuxer_t *demuxer, double rel_seek_secs, int flags)
avsflags = AVSEEK_FLAG_BACKWARD;
if (flags & SEEK_FACTOR) {
- struct stream *s = demuxer->stream;
+ struct stream *s = priv->stream;
int64_t end = stream_get_size(s);
if (end > 0 && demuxer->ts_resets_possible &&
!(priv->avif_flags & AVFMT_NO_BYTE_SEEK))
@@ -1045,6 +1062,12 @@ redo:
goto redo;
}
priv->cur_program = prog->progid = program->id;
+
+ mp_tags_copy_from_av_dictionary(demuxer->metadata, priv->avfc->programs[p]->metadata);
+ update_metadata(demuxer, NULL);
+ // Enforce metadata update even if no explicit METADATA_UPDATED since we switched program.
+ demux_changed(demuxer, DEMUX_EVENT_METADATA);
+
return DEMUXER_CTRL_OK;
}
case DEMUXER_CTRL_RESYNC:
@@ -1061,10 +1084,10 @@ redo:
*/
// avio_flush() is designed for write-only streams, and does the wrong
// thing when reading. Flush it manually instead.
- stream_drop_buffers(demuxer->stream);
+ stream_drop_buffers(priv->stream);
priv->avfc->pb->buf_ptr = priv->avfc->pb->buf_end = priv->avfc->pb->buffer;
- priv->avfc->pb->pos = stream_tell(demuxer->stream);
- av_seek_frame(priv->avfc, 0, stream_tell(demuxer->stream),
+ priv->avfc->pb->pos = stream_tell(priv->stream);
+ av_seek_frame(priv->avfc, 0, stream_tell(priv->stream),
AVSEEK_FLAG_BYTE);
return DEMUXER_CTRL_OK;
default:
@@ -1085,8 +1108,10 @@ static void demux_close_lavf(demuxer_t *demuxer)
av_freep(&priv->pb);
for (int n = 0; n < priv->num_streams; n++) {
if (priv->streams[n])
- avcodec_free_context(&priv->streams[n]->lav_headers);
+ avcodec_free_context(&priv->streams[n]->codec->lav_headers);
}
+ if (priv->stream != demuxer->stream)
+ free_stream(priv->stream);
talloc_free(priv);
demuxer->priv = NULL;
}