sub: preload external text subtitles

If a subtitle is external, read it completely and add all subtitle
events in advance when the subtitle track is selected. This is done
for text subtitles only. (Note that subreader.c and subtitles loaded
with libass are different and don't have anything to do with this
commit.)
This commit is contained in:
wm4 2013-06-11 21:39:54 +02:00
parent a792f0d628
commit a70d575291
5 changed files with 109 additions and 2 deletions

View File

@ -2036,12 +2036,22 @@ static void reinit_subs(struct MPContext *mpctx)
if (!sub_is_initialized(dec_sub)) {
int w = mpctx->sh_video ? mpctx->sh_video->disp_w : 0;
int h = mpctx->sh_video ? mpctx->sh_video->disp_h : 0;
float fps = mpctx->sh_video ? mpctx->sh_video->fps : 25;
set_dvdsub_fake_extradata(dec_sub, track->demuxer->stream, w, h);
sub_set_video_res(dec_sub, w, h);
sub_set_video_fps(dec_sub, fps);
sub_set_ass_renderer(dec_sub, mpctx->osd->ass_library,
mpctx->osd->ass_renderer);
sub_init_from_sh(dec_sub, sh_sub);
// Don't do this if the file has video/audio streams. Don't do it even
// if it has only sub streams, because reading packets will change the
// demuxer position.
if (!track->preloaded && track->is_external) {
demux_seek(track->demuxer, 0, 0, SEEK_ABSOLUTE);
track->preloaded = sub_read_all_packets(dec_sub, sh_sub);
}
}
mpctx->osd->dec_sub = dec_sub;

View File

@ -40,7 +40,8 @@
#include "audio/format.h"
#include "libavcodec/avcodec.h"
#include <libavcodec/avcodec.h>
#if MP_INPUT_BUFFER_PADDING_SIZE < FF_INPUT_BUFFER_PADDING_SIZE
#error MP_INPUT_BUFFER_PADDING_SIZE is too small!
#endif
@ -183,6 +184,41 @@ void free_demux_packet(struct demux_packet *dp)
talloc_free(dp);
}
static int destroy_avpacket(void *pkt)
{
av_free_packet(pkt);
return 0;
}
struct demux_packet *demux_copy_packet(struct demux_packet *dp)
{
struct demux_packet *new = NULL;
// No av_copy_packet() in Libav
#if LIBAVCODEC_VERSION_MICRO >= 100
if (dp->avpacket) {
assert(dp->buffer == dp->avpacket->data);
assert(dp->len == dp->avpacket->size);
AVPacket *newavp = talloc_zero(NULL, AVPacket);
talloc_set_destructor(newavp, destroy_avpacket);
av_init_packet(newavp);
if (av_copy_packet(newavp, dp->avpacket) < 0)
abort();
new = new_demux_packet_fromdata(newavp->data, newavp->size);
new->avpacket = newavp;
}
#endif
if (!new) {
new = new_demux_packet(dp->len);
memcpy(new->buffer, dp->buffer, new->len);
}
new->pts = dp->pts;
new->duration = dp->duration;
new->stream_pts = dp->stream_pts;
new->pos = dp->pos;
new->keyframe = dp->keyframe;
return new;
}
static void free_demuxer_stream(struct demux_stream *ds)
{
ds_free_packs(ds);

View File

@ -291,6 +291,7 @@ struct demux_packet *new_demux_packet_fromdata(void *data, size_t len);
struct demux_packet *new_demux_packet_from(void *data, size_t len);
void resize_demux_packet(struct demux_packet *dp, size_t len);
void free_demux_packet(struct demux_packet *dp);
struct demux_packet *demux_copy_packet(struct demux_packet *dp);
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)

View File

@ -21,7 +21,7 @@
#include <assert.h>
#include "config.h"
#include "demux/stheader.h"
#include "demux/demux.h"
#include "sd.h"
#include "sub.h"
#include "dec_sub.h"
@ -56,10 +56,17 @@ struct dec_sub {
struct MPOpts *opts;
struct sd init_sd;
double video_fps;
struct sd *sd[MAX_NUM_SD];
int num_sd;
};
struct packet_list {
struct demux_packet **packets;
int num_packets;
};
struct dec_sub *sub_create(struct MPOpts *opts)
{
struct dec_sub *sub = talloc_zero(NULL, struct dec_sub);
@ -102,6 +109,11 @@ void sub_set_video_res(struct dec_sub *sub, int w, int h)
sub->init_sd.sub_video_h = h;
}
void sub_set_video_fps(struct dec_sub *sub, double fps)
{
sub->video_fps = fps;
}
void sub_set_extradata(struct dec_sub *sub, void *data, int data_len)
{
sub->init_sd.extradata = data_len ? talloc_memdup(sub, data, data_len) : NULL;
@ -249,6 +261,52 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh)
sh->gsh->codec ? sh->gsh->codec : "<unknown>");
}
static void add_sub_list(struct dec_sub *sub, struct packet_list *subs)
{
struct sd *sd = sub_get_last_sd(sub);
assert(sd);
sd->no_remove_duplicates = true;
for (int n = 0; n < subs->num_packets; n++)
sub_decode(sub, subs->packets[n]);
// Hack for broken FFmpeg packet format: make sd_ass keep the subtitle
// events on reset(), even if broken FFmpeg ASS packets were received
// (from sd_lavc_conv.c). Normally, these events are removed on seek/reset,
// but this is obviously unwanted in this case.
if (sd->driver->fix_events)
sd->driver->fix_events(sd);
sd->no_remove_duplicates = false;
}
// Read all packets from the demuxer and decode/add them. Returns false if
// there are circumstances which makes this not possible.
bool sub_read_all_packets(struct dec_sub *sub, struct sh_sub *sh)
{
if (!sub_accept_packets_in_advance(sub) || sh->track)
return false;
void *tmp = talloc_new(NULL);
struct packet_list subs = {0};
for (;;) {
ds_get_next_pts(sh->ds);
struct demux_packet *pkt = ds_get_packet_sub(sh->ds);
if (!pkt)
break;
pkt = demux_copy_packet(pkt);
talloc_steal(tmp, pkt);
MP_TARRAY_APPEND(tmp, subs.packets, subs.num_packets, pkt);
}
add_sub_list(sub, &subs);
talloc_free(tmp);
return true;
}
bool sub_accept_packets_in_advance(struct dec_sub *sub)
{
// Converters are assumed to always accept packets in advance

View File

@ -20,6 +20,7 @@ struct dec_sub *sub_create(struct MPOpts *opts);
void sub_destroy(struct dec_sub *sub);
void sub_set_video_res(struct dec_sub *sub, int w, int h);
void sub_set_video_fps(struct dec_sub *sub, double fps);
void sub_set_extradata(struct dec_sub *sub, void *data, int data_len);
void sub_set_ass_renderer(struct dec_sub *sub, struct ass_library *ass_library,
struct ass_renderer *ass_renderer);
@ -27,6 +28,7 @@ void sub_init_from_sh(struct dec_sub *sub, struct sh_sub *sh);
bool sub_is_initialized(struct dec_sub *sub);
bool sub_read_all_packets(struct dec_sub *sub, struct sh_sub *sh);
bool sub_accept_packets_in_advance(struct dec_sub *sub);
void sub_decode(struct dec_sub *sub, struct demux_packet *packet);
void sub_get_bitmaps(struct dec_sub *sub, struct mp_osd_res dim, double pts,