mirror of
https://github.com/mpv-player/mpv
synced 2024-12-28 10:02:17 +00:00
f5219720f8
Refactor the PTS handling code to make it cleaner, and to separate the bits that use PTS sorting. Add a heuristic to fall back to DTS if the PTS us non-monotonic. This code is based on what FFmpeg/Libav use for ffplay/avplay and also best_effort_timestamp (which is only in FFmpeg). Basically, this 1. just uses the DTS if PTS is unset, and 2. ignores PTS entirely if PTS is non- monotonic, but DTS is sorted. The code is pretty much the same as in Libav [1]. I'm not sure if all of it is really needed, or if it does more than what the paragraph above mentions. But maybe it's fine to cargo-cult this. This heuristic fixes playback of mpeg4 in ogm, which returns packets with PTS==DTS, even though the PTS timestamps should follow codec reordering. This is probably a libavformat demuxer bug, but good luck trying to fix it. The way vd_lavc.c returns the frame PTS and DTS to dec_video.c is a bit inelegant, but maybe better than trying to mess the PTS back into the decoder callback again. [1] https://git.libav.org/?p=libav.git;a=blob;f=cmdutils.c;h=3f1c667075724c5cde69d840ed5ed7d992898334;hb=fa515c2088e1d082d45741bbd5c05e13b0500804#l1431
143 lines
5.1 KiB
C
143 lines
5.1 KiB
C
/*
|
|
* 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 <assert.h>
|
|
|
|
#include <libavutil/common.h>
|
|
#include <libavcodec/avcodec.h>
|
|
|
|
#include "mpvcore/mp_common.h"
|
|
#include "demux/packet.h"
|
|
#include "av_common.h"
|
|
#include "codecs.h"
|
|
|
|
|
|
// Copy the codec-related fields from st into avctx. This does not set the
|
|
// codec itself, only codec related header data provided by libavformat.
|
|
// The goal is to initialize a new decoder with the header data provided by
|
|
// libavformat, and unlike avcodec_copy_context(), allow the user to create
|
|
// a clean AVCodecContext for a manually selected AVCodec.
|
|
// This is strictly for decoding only.
|
|
void mp_copy_lav_codec_headers(AVCodecContext *avctx, AVCodecContext *st)
|
|
{
|
|
if (st->extradata_size) {
|
|
av_free(avctx->extradata);
|
|
avctx->extradata_size = 0;
|
|
avctx->extradata =
|
|
av_mallocz(st->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
|
|
if (avctx->extradata) {
|
|
avctx->extradata_size = st->extradata_size;
|
|
memcpy(avctx->extradata, st->extradata, st->extradata_size);
|
|
}
|
|
}
|
|
avctx->codec_tag = st->codec_tag;
|
|
avctx->stream_codec_tag = st->stream_codec_tag;
|
|
avctx->bit_rate = st->bit_rate;
|
|
avctx->width = st->width;
|
|
avctx->height = st->height;
|
|
avctx->pix_fmt = st->pix_fmt;
|
|
avctx->sample_aspect_ratio = st->sample_aspect_ratio;
|
|
avctx->chroma_sample_location = st->chroma_sample_location;
|
|
avctx->sample_rate = st->sample_rate;
|
|
avctx->channels = st->channels;
|
|
avctx->block_align = st->block_align;
|
|
avctx->channel_layout = st->channel_layout;
|
|
avctx->audio_service_type = st->audio_service_type;
|
|
avctx->bits_per_coded_sample = st->bits_per_coded_sample;
|
|
}
|
|
|
|
// We merely pass-through our PTS/DTS as an int64_t; libavcodec won't use it.
|
|
union pts { int64_t i; double d; };
|
|
|
|
// Set dst from mpkt. Note that dst is not refcountable.
|
|
// mpkt can be NULL to generate empty packets (used to flush delayed data).
|
|
// Sets pts/dts to reinterpret-casted mpv values - these are useful for
|
|
// reading back via mp_get_av_frame_pkt_pdts(), but they will be non-sense for
|
|
// libavcodec itself. (And normally, libavcodec won't interpret them.)
|
|
// Does not set duration field.
|
|
void mp_set_av_packet(AVPacket *dst, struct demux_packet *mpkt)
|
|
{
|
|
assert(sizeof(int64_t) >= sizeof(double));
|
|
|
|
av_init_packet(dst);
|
|
dst->data = mpkt ? mpkt->buffer : NULL;
|
|
dst->size = mpkt ? mpkt->len : 0;
|
|
/* Some codecs (ZeroCodec, some cases of PNG) may want keyframe info
|
|
* from demuxer. */
|
|
if (mpkt && mpkt->keyframe)
|
|
dst->flags |= AV_PKT_FLAG_KEY;
|
|
if (mpkt && mpkt->avpacket) {
|
|
dst->side_data = mpkt->avpacket->side_data;
|
|
dst->side_data_elems = mpkt->avpacket->side_data_elems;
|
|
}
|
|
dst->pts = (union pts){.d = mpkt ? mpkt->pts : MP_NOPTS_VALUE}.i;
|
|
dst->dts = (union pts){.d = mpkt ? mpkt->dts : MP_NOPTS_VALUE}.i;
|
|
}
|
|
|
|
// Return the pts/dts from a frame returned by libavcodec. Note that this
|
|
// assumes libavcodec was fed a packet setup with mp_set_av_packet()! If not,
|
|
// the timestamps might contain garbage.
|
|
void mp_get_av_frame_pkt_ts(AVFrame *frame, double *out_pts, double *out_dts)
|
|
{
|
|
*out_pts = (union pts){.i = frame->pkt_pts}.d;
|
|
*out_dts = (union pts){.i = frame->pkt_dts}.d;
|
|
}
|
|
|
|
void mp_add_lavc_decoders(struct mp_decoder_list *list, enum AVMediaType type)
|
|
{
|
|
AVCodec *cur = NULL;
|
|
for (;;) {
|
|
cur = av_codec_next(cur);
|
|
if (!cur)
|
|
break;
|
|
if (av_codec_is_decoder(cur) && cur->type == type) {
|
|
mp_add_decoder(list, "lavc", mp_codec_from_av_codec_id(cur->id),
|
|
cur->name, cur->long_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
int mp_codec_to_av_codec_id(const char *codec)
|
|
{
|
|
int id = AV_CODEC_ID_NONE;
|
|
if (codec) {
|
|
const AVCodecDescriptor *desc = avcodec_descriptor_get_by_name(codec);
|
|
if (desc)
|
|
id = desc->id;
|
|
if (id == AV_CODEC_ID_NONE) {
|
|
AVCodec *avcodec = avcodec_find_decoder_by_name(codec);
|
|
if (avcodec)
|
|
id = avcodec->id;
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
const char *mp_codec_from_av_codec_id(int codec_id)
|
|
{
|
|
const char *name = NULL;
|
|
const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id);
|
|
if (desc)
|
|
name = desc->name;
|
|
if (!name) {
|
|
AVCodec *avcodec = avcodec_find_decoder(codec_id);
|
|
if (avcodec)
|
|
name = avcodec->name;
|
|
}
|
|
return name;
|
|
}
|