mirror of
https://github.com/mpv-player/mpv
synced 2025-03-25 04:38:01 +00:00
The main thing this commit does is removing demux_packet.kf_seek_pts. It gets rid of 8 bytes per packet. Which doesn't matter, but whatever. This field was involved with much of seek range updating and pruning, because it tracked the canonical seek PTS (i.e. start PTS) of a packet range. We have to deal with timestamp reordering, and assume the start PTS is the lowest PTS across all packets (not necessarily just the first packet). So knowing this PTS requires looping over all packets of a range (no, the demuxer isn't going to tell us, that would be too sane). Having this as packet field was perfectly fine. I'm just removing it because I started hating extra packet fields recently. Before this commit, this value was cached in the kf_seek_pts field (and computed "incrementally" when adding packets). This commit computes the value on demand (compute_keyframe_times()) by iterating over the placket list. There is some similarity with the state before10d0963d85
, where I introduced the kf_seek_pts field - maybe I'm just moving in circles. The commit message claims something about quadratic complexity, but if the code before that had this problem, this new commit doesn't reintroduce it, at least. (See below.) The pruning logic is simplified (I think?) - there is no "incremental" cached pruning decision anymore (next_prune_target is removed), and instead it simply prunes until the next keyframe like it's supposed to. I think this incremental stuff was only there because of very old code that got refactored away before. I don't even know what I was thinking there, it just seems complex. Now the seek range is updated when a keyframe packet is removed. Instead of using the kf_seek_pts field, queue->seek_start is used to determine the stream with the lowest timestamp, which should be pruned first. This is different, but should work well. Doing the same as the previous code would require compute_keyframe_times(), which would introduce quadratic complexity. On the other hand, it's fine to call compute_keyframe_times() when the seek range is recomputed on pruning, because this is called only once per removed keyframe packet. Effectively, this will iterate over the packet list twice instead of once, and with some locality. The same happens when packets are appended - it loops over the recently added packets once again. (And not more often, which would go above linear complexity.) This introduces some "cleverness" with avoiding calling update_seek_ranges() even when keyframe packets added/removed, which is not really tightly coupled to the new code, and could have been in a separate commit. Removing next_prune_target achieves the same as commitb275232141
, which is hereby reverted (stale is_bof flags prevent seeking before the current range, even if the beginning of the file was pruned). The seek range is now strictly computed after at least one packet was removed, and stale state should not be possible anymore. Range joining may over-allocate the index a little. It tried hard to avoid this before by explicitly freeing the old index before creating a new one. Now it iterates over the old index while adding the entries to the new one, which is simpler, but may allocate twice the memory in the worst case. It's not going to matter for anything, though. Seeking will be slightly slower. It needs to compute the seek PTS values across all packets in the vicinity of the seek target. The previous code also iterated over these packets, but now it iterates one packet range more. Another minor detail is that the special seeking code for SEEK_FORWARD goes away. The seeking code will now iterate over the very last packet range too, even if it's incomplete (i.e. packets are still being appended to it). It's fine that it touches the incomplete range, because the seek_end fields prevent that anything particularly incorrect can happen. On the other hand, SEEK_FORWARD can now consider this as seek target, which the deleted code had to do explicitly, as kf_seek_pts was unset for incomplete packet ranges.
211 lines
6.2 KiB
C
211 lines
6.2 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 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavutil/intreadwrite.h>
|
|
|
|
#include "config.h"
|
|
|
|
#include "common/av_common.h"
|
|
#include "common/common.h"
|
|
#include "demux.h"
|
|
|
|
#include "packet.h"
|
|
|
|
static void packet_destroy(void *ptr)
|
|
{
|
|
struct demux_packet *dp = ptr;
|
|
av_packet_unref(dp->avpacket);
|
|
}
|
|
|
|
// This actually preserves only data and side data, not PTS/DTS/pos/etc.
|
|
// It also allows avpkt->data==NULL with avpkt->size!=0 - the libavcodec API
|
|
// does not allow it, but we do it to simplify new_demux_packet().
|
|
struct demux_packet *new_demux_packet_from_avpacket(struct AVPacket *avpkt)
|
|
{
|
|
if (avpkt->size > 1000000000)
|
|
return NULL;
|
|
struct demux_packet *dp = talloc(NULL, struct demux_packet);
|
|
talloc_set_destructor(dp, packet_destroy);
|
|
*dp = (struct demux_packet) {
|
|
.pts = MP_NOPTS_VALUE,
|
|
.dts = MP_NOPTS_VALUE,
|
|
.duration = -1,
|
|
.pos = -1,
|
|
.start = MP_NOPTS_VALUE,
|
|
.end = MP_NOPTS_VALUE,
|
|
.stream = -1,
|
|
.avpacket = talloc_zero(dp, AVPacket),
|
|
};
|
|
av_init_packet(dp->avpacket);
|
|
int r = -1;
|
|
if (avpkt->data) {
|
|
// We hope that this function won't need/access AVPacket input padding,
|
|
// because otherwise new_demux_packet_from() wouldn't work.
|
|
r = av_packet_ref(dp->avpacket, avpkt);
|
|
} else {
|
|
r = av_new_packet(dp->avpacket, avpkt->size);
|
|
}
|
|
if (r < 0) {
|
|
*dp->avpacket = (AVPacket){0};
|
|
talloc_free(dp);
|
|
return NULL;
|
|
}
|
|
dp->buffer = dp->avpacket->data;
|
|
dp->len = dp->avpacket->size;
|
|
return dp;
|
|
}
|
|
|
|
// (buf must include proper padding)
|
|
struct demux_packet *new_demux_packet_from_buf(struct AVBufferRef *buf)
|
|
{
|
|
if (!buf)
|
|
return NULL;
|
|
AVPacket pkt = {
|
|
.size = buf->size,
|
|
.data = buf->data,
|
|
.buf = buf,
|
|
};
|
|
return new_demux_packet_from_avpacket(&pkt);
|
|
}
|
|
|
|
// Input data doesn't need to be padded.
|
|
struct demux_packet *new_demux_packet_from(void *data, size_t len)
|
|
{
|
|
if (len > INT_MAX)
|
|
return NULL;
|
|
AVPacket pkt = { .data = data, .size = len };
|
|
return new_demux_packet_from_avpacket(&pkt);
|
|
}
|
|
|
|
struct demux_packet *new_demux_packet(size_t len)
|
|
{
|
|
if (len > INT_MAX)
|
|
return NULL;
|
|
AVPacket pkt = { .data = NULL, .size = len };
|
|
return new_demux_packet_from_avpacket(&pkt);
|
|
}
|
|
|
|
void demux_packet_shorten(struct demux_packet *dp, size_t len)
|
|
{
|
|
assert(len <= dp->len);
|
|
av_shrink_packet(dp->avpacket, len);
|
|
dp->len = dp->avpacket->size;
|
|
}
|
|
|
|
void free_demux_packet(struct demux_packet *dp)
|
|
{
|
|
talloc_free(dp);
|
|
}
|
|
|
|
void demux_packet_copy_attribs(struct demux_packet *dst, struct demux_packet *src)
|
|
{
|
|
dst->pts = src->pts;
|
|
dst->dts = src->dts;
|
|
dst->duration = src->duration;
|
|
dst->pos = src->pos;
|
|
dst->segmented = src->segmented;
|
|
dst->start = src->start;
|
|
dst->end = src->end;
|
|
dst->codec = src->codec;
|
|
dst->back_restart = src->back_restart;
|
|
dst->back_preroll = src->back_preroll;
|
|
dst->keyframe = src->keyframe;
|
|
dst->stream = src->stream;
|
|
}
|
|
|
|
struct demux_packet *demux_copy_packet(struct demux_packet *dp)
|
|
{
|
|
struct demux_packet *new = NULL;
|
|
if (dp->avpacket) {
|
|
new = new_demux_packet_from_avpacket(dp->avpacket);
|
|
} else {
|
|
// Some packets might be not created by new_demux_packet*().
|
|
new = new_demux_packet_from(dp->buffer, dp->len);
|
|
}
|
|
if (!new)
|
|
return NULL;
|
|
demux_packet_copy_attribs(new, dp);
|
|
return new;
|
|
}
|
|
|
|
#define ROUND_ALLOC(s) MP_ALIGN_UP(s, 64)
|
|
|
|
// Attempt to estimate the total memory consumption of the given packet.
|
|
// This is important if we store thousands of packets and not to exceed
|
|
// user-provided limits. Of course we can't know how much memory internal
|
|
// fragmentation of the libc memory allocator will waste.
|
|
// Note that this should return a "stable" value - e.g. if a new packet ref
|
|
// is created, this should return the same value with the new ref. (This
|
|
// implies the value is not exact and does not return the actual size of
|
|
// memory wasted due to internal fragmentation.)
|
|
size_t demux_packet_estimate_total_size(struct demux_packet *dp)
|
|
{
|
|
size_t size = ROUND_ALLOC(sizeof(struct demux_packet));
|
|
size += ROUND_ALLOC(dp->len);
|
|
if (dp->avpacket) {
|
|
size += ROUND_ALLOC(sizeof(AVPacket));
|
|
size += ROUND_ALLOC(sizeof(AVBufferRef));
|
|
size += 64; // upper bound estimate on sizeof(AVBuffer)
|
|
size += ROUND_ALLOC(dp->avpacket->side_data_elems *
|
|
sizeof(dp->avpacket->side_data[0]));
|
|
for (int n = 0; n < dp->avpacket->side_data_elems; n++)
|
|
size += ROUND_ALLOC(dp->avpacket->side_data[n].size);
|
|
}
|
|
return size;
|
|
}
|
|
|
|
int demux_packet_set_padding(struct demux_packet *dp, int start, int end)
|
|
{
|
|
#if LIBAVCODEC_VERSION_MICRO >= 100
|
|
if (!start && !end)
|
|
return 0;
|
|
if (!dp->avpacket)
|
|
return -1;
|
|
uint8_t *p = av_packet_new_side_data(dp->avpacket, AV_PKT_DATA_SKIP_SAMPLES, 10);
|
|
if (!p)
|
|
return -1;
|
|
|
|
AV_WL32(p + 0, start);
|
|
AV_WL32(p + 4, end);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int demux_packet_add_blockadditional(struct demux_packet *dp, uint64_t id,
|
|
void *data, size_t size)
|
|
{
|
|
#if LIBAVCODEC_VERSION_MICRO >= 100
|
|
if (!dp->avpacket)
|
|
return -1;
|
|
uint8_t *sd = av_packet_new_side_data(dp->avpacket,
|
|
AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
|
|
8 + size);
|
|
if (!sd)
|
|
return -1;
|
|
AV_WB64(sd, id);
|
|
if (size > 0)
|
|
memcpy(sd + 8, data, size);
|
|
#endif
|
|
return 0;
|
|
}
|