From 84f7e213ab6ae1736c7e4ad5b2ff6d002c554361 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 3 Nov 2013 17:30:34 +0100 Subject: stream: reconnecting doesn't make sense if stream is not seekable This stops mpv from being stuck in reconnecting at the end of the file with some unseekable streams. Test URL: http://playerservices.streamtheworld.com/pls/CBC_R1_VCR_H.pls --- stream/stream.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stream/stream.c b/stream/stream.c index 186fc7c79d..01c1819dbb 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -387,6 +387,8 @@ static int stream_reconnect(stream_t *s) #define RECONNECT_SLEEP_MS 1000 if (!s->streaming) return 0; + if (!s->end_pos || !s->seek || !(s->flags & MP_STREAM_SEEK)) + return 0; int64_t pos = s->pos; for (int retry = 0; retry < MAX_RECONNECT_RETRIES; retry++) { mp_msg(MSGT_STREAM, MSGL_WARN, -- cgit v1.2.3 From 22274f7982e01784a27daac371fe2a1607433425 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 3 Nov 2013 17:46:36 +0100 Subject: stream: more consistent checks for whether stream is seekable Never check s->seek (except in init), because it'd have to check s->flags anyway. Also, for fast skippable streams (like pipes), don't set the bit that indicates support for seek forward. Make sure s->end_pos is always 0 for unseekable streams. Lots of code outside of stream.c uses this to check seeking support. --- stream/stream.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/stream/stream.c b/stream/stream.c index 01c1819dbb..bf2adeb564 100644 --- a/stream/stream.c +++ b/stream/stream.c @@ -318,8 +318,8 @@ static int open_internal(const stream_info_t *sinfo, struct stream *underlying, if (s->seek && !(s->flags & MP_STREAM_SEEK)) s->flags |= MP_STREAM_SEEK; - if (s->flags & MP_STREAM_FAST_SKIPPING) - s->flags |= MP_STREAM_SEEK_FW; + if (!(s->flags & MP_STREAM_SEEK)) + s->end_pos = 0; s->uncached_type = s->type; @@ -387,7 +387,7 @@ static int stream_reconnect(stream_t *s) #define RECONNECT_SLEEP_MS 1000 if (!s->streaming) return 0; - if (!s->end_pos || !s->seek || !(s->flags & MP_STREAM_SEEK)) + if (!(s->flags & MP_STREAM_SEEK_FW)) return 0; int64_t pos = s->pos; for (int retry = 0; retry < MAX_RECONNECT_RETRIES; retry++) { @@ -603,7 +603,7 @@ static int stream_skip_read(struct stream *s, int64_t len) static int stream_seek_unbuffered(stream_t *s, int64_t newpos) { if (newpos != s->pos) { - if (!s->seek || !(s->flags & MP_STREAM_SEEK)) { + if (newpos > s->pos && !(s->flags & MP_STREAM_SEEK_FW)) { mp_tmsg(MSGT_STREAM, MSGL_ERR, "Can not seek in this stream\n"); return 0; } @@ -630,7 +630,7 @@ static int stream_seek_long(stream_t *s, int64_t pos) s->eof = 0; if (s->mode == STREAM_WRITE) { - if (!s->seek || !s->seek(s, pos)) + if (!(s->flags & MP_STREAM_SEEK) || !s->seek(s, pos)) return 0; return 1; } @@ -642,7 +642,9 @@ static int stream_seek_long(stream_t *s, int64_t pos) mp_msg(MSGT_STREAM, MSGL_DBG3, "Seek from %" PRId64 " to %" PRId64 " (with offset %d)\n", s->pos, pos, (int)(pos - newpos)); - if (!s->seek && (s->flags & MP_STREAM_FAST_SKIPPING) && pos >= s->pos) { + if (pos >= s->pos && !(s->flags & MP_STREAM_SEEK) && + (s->flags & MP_STREAM_FAST_SKIPPING)) + { // skipping is handled by generic code below } else if (stream_seek_unbuffered(s, newpos) >= 0) { return 0; @@ -711,6 +713,8 @@ int stream_control(stream_t *s, int cmd, void *arg) void stream_update_size(stream_t *s) { + if (!(s->flags & MP_STREAM_SEEK)) + return; uint64_t size; if (stream_control(s, STREAM_CTRL_GET_SIZE, &size) == STREAM_OK) { if (size > s->end_pos) -- cgit v1.2.3 From 847cbe9d5d03c77491f3139dde6e163426283ccb Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 3 Nov 2013 18:50:00 +0100 Subject: demux: remove movi_start/movi_end fields Pointless, using stream->start_pos/end_pos instead. demux_mf was the only place where this was used specially, but we can rely on timestamps instead for this case. --- demux/demux.c | 2 -- demux/demux.h | 2 -- demux/demux_lavf.c | 12 ++++++------ demux/demux_mf.c | 4 ---- demux/demux_mkv.c | 8 +++----- demux/demux_raw.c | 8 +------- mpvcore/player/playloop.c | 5 +++-- 7 files changed, 13 insertions(+), 28 deletions(-) diff --git a/demux/demux.c b/demux/demux.c index ba632218ec..1ce61da396 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -533,8 +533,6 @@ static struct demuxer *open_given_type(struct MPOpts *opts, .type = desc->type, .stream = stream, .stream_pts = MP_NOPTS_VALUE, - .movi_start = stream->start_pos, - .movi_end = stream->end_pos, .seekable = 1, .accurate_seek = true, .filepos = -1, diff --git a/demux/demux.h b/demux/demux.h index 0c8f9cb3f8..1c8562eb63 100644 --- a/demux/demux.h +++ b/demux/demux.h @@ -166,8 +166,6 @@ typedef struct demuxer { const demuxer_desc_t *desc; ///< Demuxer description structure const char *filetype; // format name when not identified by demuxer (libavformat) int64_t filepos; // input stream current pos. - int64_t movi_start; - int64_t movi_end; struct stream *stream; double stream_pts; // current stream pts, if applicable (e.g. dvd) char *filename; // same as stream->url diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index e80db5aed4..117dcd48ed 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -857,12 +857,12 @@ static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs, int flags) avsflags = AVSEEK_FLAG_BACKWARD; if (flags & SEEK_FACTOR) { - if (demuxer->movi_end > 0 && demuxer->ts_resets_possible && + struct stream *s = demuxer->stream; + if (s->end_pos > 0 && demuxer->ts_resets_possible && !(priv->avif->flags & AVFMT_NO_BYTE_SEEK)) { avsflags |= AVSEEK_FLAG_BYTE; - priv->last_pts = (demuxer->movi_end - demuxer->movi_start) * - rel_seek_secs; + priv->last_pts = (s->end_pos - s->start_pos) * rel_seek_secs; } else if (priv->avfc->duration != 0 && priv->avfc->duration != AV_NOPTS_VALUE) { @@ -923,12 +923,12 @@ static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg) switch (cmd) { case DEMUXER_CTRL_GET_TIME_LENGTH: if (priv->seek_by_bytes) { + struct stream *s = demuxer->stream; /* Our bitrate estimate may be better than would be used in * otherwise similar fallback code at higher level */ - if (demuxer->movi_end <= 0) + if (s->end_pos <= 0) return DEMUXER_CTRL_DONTKNOW; - *(double *)arg = (demuxer->movi_end - demuxer->movi_start) * 8 / - priv->bitrate; + *(double *)arg = (s->end_pos - s->start_pos) * 8 / priv->bitrate; return DEMUXER_CTRL_GUESS; } if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) diff --git a/demux/demux_mf.c b/demux/demux_mf.c index 0d7b2ece8a..fe8a249320 100644 --- a/demux/demux_mf.c +++ b/demux/demux_mf.c @@ -85,7 +85,6 @@ static int demux_mf_fill_buffer(demuxer_t *demuxer) demux_packet_t *dp = new_demux_packet(data.len); memcpy(dp->buffer, data.start, data.len); dp->pts = mf->curr_frame / mf->sh->fps; - dp->pos = mf->curr_frame; dp->keyframe = true; demuxer_add_packet(demuxer, demuxer->streams[0], dp); } @@ -200,9 +199,6 @@ static int demux_open_mf(demuxer_t* demuxer, enum demux_check check) mf->curr_frame = 0; - demuxer->movi_start = 0; - demuxer->movi_end = mf->nr_of_files - 1; - // create a new video stream header struct sh_stream *sh = new_sh_stream(demuxer, STREAM_VIDEO); sh_video = sh->video; diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c index b26cba5a29..d6948e9979 100644 --- a/demux/demux_mkv.c +++ b/demux/demux_mkv.c @@ -1029,7 +1029,7 @@ static int demux_mkv_read_seekhead(demuxer_t *demuxer) continue; } uint64_t pos = seek->seek_position + mkv_d->segment_start; - if (pos >= demuxer->movi_end) { + if (pos >= s->end_pos) { mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] SeekHead position beyond " "end of file - incomplete file?\n"); continue; @@ -1847,8 +1847,6 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check) if (s->end_pos == 0) { demuxer->seekable = 0; } else { - demuxer->movi_start = s->start_pos; - demuxer->movi_end = s->end_pos; demuxer->seekable = 1; } @@ -2713,7 +2711,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags) } demux_mkv_fill_buffer(demuxer); - } else if ((demuxer->movi_end <= 0) || !(flags & SEEK_ABSOLUTE)) + } else if (!(flags & SEEK_ABSOLUTE)) mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] seek unsupported flags\n"); else { stream_t *s = demuxer->stream; @@ -2729,7 +2727,7 @@ static void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs, int flags) return; } - target_filepos = (uint64_t) (demuxer->movi_end * rel_seek_secs); + target_filepos = (uint64_t) (s->end_pos * rel_seek_secs); for (i = 0; i < mkv_d->num_indexes; i++) if (mkv_d->indexes[i].tnum == v_tnum) if ((index == NULL) diff --git a/demux/demux_raw.c b/demux/demux_raw.c index 4d6e171ad4..01cf3574a0 100644 --- a/demux/demux_raw.c +++ b/demux/demux_raw.c @@ -100,9 +100,6 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check) w->wBitsPerSample = 8 * samplesize; w->cbSize = 0; - demuxer->movi_start = demuxer->stream->start_pos; - demuxer->movi_end = demuxer->stream->end_pos; - struct priv *p = talloc_ptrtype(demuxer, p); demuxer->priv = p; *p = (struct priv) { @@ -185,9 +182,6 @@ static int demux_rawvideo_open(demuxer_t *demuxer, enum demux_check check) sh_video->disp_h = height; sh_video->i_bps = fps * imgsize; - demuxer->movi_start = demuxer->stream->start_pos; - demuxer->movi_end = demuxer->stream->end_pos; - struct priv *p = talloc_ptrtype(demuxer, p); demuxer->priv = p; *p = (struct priv) { @@ -207,7 +201,7 @@ static int raw_fill_buffer(demuxer_t *demuxer) return 0; struct demux_packet *dp = new_demux_packet(p->frame_size * p->read_frames); - dp->pos = stream_tell(demuxer->stream) - demuxer->movi_start; + dp->pos = stream_tell(demuxer->stream) - demuxer->stream->start_pos; dp->pts = (dp->pos / p->frame_size) / p->frame_rate; int len = stream_read(demuxer->stream, dp->buffer, dp->len); diff --git a/mpvcore/player/playloop.c b/mpvcore/player/playloop.c index 8efb6d6693..9e5aa1e0b4 100644 --- a/mpvcore/player/playloop.c +++ b/mpvcore/player/playloop.c @@ -452,11 +452,12 @@ double get_current_pos_ratio(struct MPContext *mpctx, bool use_range) if (len > 0 && !demuxer->ts_resets_possible) { ans = MPCLAMP((pos - start) / len, 0, 1); } else { - int64_t size = (demuxer->movi_end - demuxer->movi_start); + struct stream *s = demuxer->stream; + int64_t size = s->end_pos - s->start_pos; int64_t fpos = demuxer->filepos > 0 ? demuxer->filepos : stream_tell(demuxer->stream); if (size > 0) - ans = MPCLAMP((double)(fpos - demuxer->movi_start) / size, 0, 1); + ans = MPCLAMP((double)(fpos - s->start_pos) / size, 0, 1); } if (use_range) { if (mpctx->opts->play_frames > 0) -- cgit v1.2.3 From a49ab7cc2f9548edcfdd8ad9874050bd41e0b551 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 3 Nov 2013 19:21:47 +0100 Subject: demux: make determining seek capability generic Instead of having each demuxer do it (only demux_mkv actually did...), let generic code determine whether the file is seekable. This requires adding exceptions to demuxers where the stream is not seekable, but the demuxer is. Sort-of try to improve handling of unseekable files in the player. Exit early if the file is determined to be unseekable, instead of resetting all decoders and then performing a pointless seek. Add an exception to allow seeking if the file is not seekable, but the stream cache is enabled. Print a warning in this case, because seeking outside the cache (which we can't prevent since the demuxer is not aware of this problem) still messes everything up. --- demux/demux.c | 11 +++++++++-- demux/demux_lavf.c | 11 ++++++----- demux/demux_libass.c | 2 ++ demux/demux_mf.c | 1 + demux/demux_mkv.c | 6 ------ demux/demux_subreader.c | 2 ++ mpvcore/player/playloop.c | 5 +++++ 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/demux/demux.c b/demux/demux.c index 1ce61da396..c6b0ebeb4e 100644 --- a/demux/demux.c +++ b/demux/demux.c @@ -533,7 +533,8 @@ static struct demuxer *open_given_type(struct MPOpts *opts, .type = desc->type, .stream = stream, .stream_pts = MP_NOPTS_VALUE, - .seekable = 1, + .seekable = (stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK && + stream->end_pos > 0, .accurate_seek = true, .filepos = -1, .opts = opts, @@ -564,6 +565,12 @@ static struct demuxer *open_given_type(struct MPOpts *opts, add_stream_chapters(demuxer); demuxer_sort_chapters(demuxer); demux_info_update(demuxer); + // Pretend we can seek if we can't seek, but there's a cache. + if (!demuxer->seekable && stream->uncached_stream) { + mp_msg(MSGT_DEMUXER, MSGL_WARN, + "File is not seekable, but there's a cache: enabling seeking.\n"); + demuxer->seekable = true; + } return demuxer; } @@ -632,7 +639,7 @@ void demux_flush(demuxer_t *demuxer) int demux_seek(demuxer_t *demuxer, float rel_seek_secs, int flags) { if (!demuxer->seekable) { - mp_tmsg(MSGT_SEEK, MSGL_WARN, "Cannot seek in this file.\n"); + mp_tmsg(MSGT_DEMUXER, MSGL_WARN, "Cannot seek in this file.\n"); return 0; } diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index 117dcd48ed..b2dfcb2193 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -584,9 +584,12 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check) } } - if (!(priv->avif->flags & AVFMT_NOFILE) && - demuxer->stream->type != STREAMTYPE_AVDEVICE) + if ((priv->avif->flags & AVFMT_NOFILE) || + demuxer->stream->type == STREAMTYPE_AVDEVICE) { + // This might be incorrect. + demuxer->seekable = true; + } else { void *buffer = av_malloc(lavfdopts->buffersize); if (!buffer) return -1; @@ -597,9 +600,7 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check) return -1; } priv->pb->read_seek = mp_read_seek; - priv->pb->seekable = demuxer->stream->end_pos - && (demuxer->stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK - ? AVIO_SEEKABLE_NORMAL : 0; + priv->pb->seekable = demuxer->seekable ? AVIO_SEEKABLE_NORMAL : 0; avfc->pb = priv->pb; } diff --git a/demux/demux_libass.c b/demux/demux_libass.c index d98cdbb5a4..73c8b3a5c8 100644 --- a/demux/demux_libass.c +++ b/demux/demux_libass.c @@ -100,6 +100,8 @@ static int d_check_file(struct demuxer *demuxer, enum demux_check check) sh->sub->track = track; sh->codec = "ass"; + demuxer->seekable = true; + return 0; } diff --git a/demux/demux_mf.c b/demux/demux_mf.c index fe8a249320..50888f59ad 100644 --- a/demux/demux_mf.c +++ b/demux/demux_mf.c @@ -210,6 +210,7 @@ static int demux_open_mf(demuxer_t* demuxer, enum demux_check check) mf->sh = sh_video; demuxer->priv=(void*)mf; + demuxer->seekable = true; return 0; diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c index d6948e9979..4181aa4850 100644 --- a/demux/demux_mkv.c +++ b/demux/demux_mkv.c @@ -1844,12 +1844,6 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check) display_create_tracks(demuxer); - if (s->end_pos == 0) { - demuxer->seekable = 0; - } else { - demuxer->seekable = 1; - } - return 0; } diff --git a/demux/demux_subreader.c b/demux/demux_subreader.c index cc02e61595..21bcf21614 100644 --- a/demux/demux_subreader.c +++ b/demux/demux_subreader.c @@ -1358,6 +1358,8 @@ static int d_open_file(struct demuxer *demuxer, enum demux_check check) add_sub_data(demuxer, sd); subdata_free(sd); + demuxer->seekable = true; + return 0; } diff --git a/mpvcore/player/playloop.c b/mpvcore/player/playloop.c index 9e5aa1e0b4..90525a5ca2 100644 --- a/mpvcore/player/playloop.c +++ b/mpvcore/player/playloop.c @@ -215,6 +215,11 @@ static int seek(MPContext *mpctx, struct seek_params seek, if (!mpctx->demuxer) return -1; + if (!mpctx->demuxer->seekable) { + MP_ERR(mpctx, "Can't seek in this file.\n"); + return -1; + } + if (mpctx->stop_play == AT_END_OF_FILE) mpctx->stop_play = KEEP_PLAYING; bool hr_seek = mpctx->demuxer->accurate_seek && opts->correct_pts; -- cgit v1.2.3 From 388f43f6f7f4ad29dbd2626f07a15b8c1f6f9233 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 3 Nov 2013 19:22:13 +0100 Subject: player: fix quvi 0.9 playlist loading The code made no sense at all. --- mpvcore/player/loadfile.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mpvcore/player/loadfile.c b/mpvcore/player/loadfile.c index 876994ac93..d6347e24c1 100644 --- a/mpvcore/player/loadfile.c +++ b/mpvcore/player/loadfile.c @@ -894,8 +894,9 @@ static void print_resolve_contents(struct mp_log *log, // from the given playlist pl, so the entries don't actually need to be copied. static void transfer_playlist(struct MPContext *mpctx, struct playlist *pl) { - if (mpctx->demuxer->playlist->first) { - playlist_transfer_entries(mpctx->playlist, mpctx->demuxer->playlist); + if (pl->first) { + playlist_transfer_entries(mpctx->playlist, pl); + // current entry is replaced if (mpctx->playlist->current) playlist_remove(mpctx->playlist, mpctx->playlist->current); } else { -- cgit v1.2.3 From 3b5f8ea571b2227097840ff3e0be0168fdd813eb Mon Sep 17 00:00:00 2001 From: ChrisK2 Date: Sun, 3 Nov 2013 20:48:49 +0100 Subject: tools: add --no-cache --no-config to mpv_identify.sh --- TOOLS/mpv_identify.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TOOLS/mpv_identify.sh b/TOOLS/mpv_identify.sh index 0c4dfc5b02..b1c17f75c2 100755 --- a/TOOLS/mpv_identify.sh +++ b/TOOLS/mpv_identify.sh @@ -104,7 +104,7 @@ for __midentify__key in $__midentify__allprops; do eval unset $__midentify__nextprefix$__midentify__key done -__midentify__output=`$MPV --playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet "$@"` +__midentify__output=`$MPV --playing-msg="$__midentify__propstr" --vo=null --ao=null --frames=1 --quiet --no-cache --no-config "$@"` __midentify__fileindex=0 __midentify__prefix= while :; do -- cgit v1.2.3 From 6f17410f88fd3765b6598b4e706b1d03ee85efe8 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 3 Nov 2013 23:55:16 +0100 Subject: mp_image: add helper for copying image attributes This enw function is similar to mp_image_set_params(), but doesn't force setting "hard" parameters like image size and format. --- video/mp_image.c | 22 ++++++++++++++++++++++ video/mp_image.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/video/mp_image.c b/video/mp_image.c index 3bc76b669e..9b96bab67d 100644 --- a/video/mp_image.c +++ b/video/mp_image.c @@ -36,6 +36,8 @@ #include "memcpy_pic.h" #include "fmt-conversion.h" +#include "video/filter/vf.h" + #if HAVE_PTHREADS #include static pthread_mutex_t refcount_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -468,6 +470,26 @@ void mp_image_set_params(struct mp_image *image, image->chroma_location = params->chroma_location; } +// Set most image parameters, but not image format or size. +// Display size is used to set the PAR. +void mp_image_set_attributes(struct mp_image *image, + const struct mp_image_params *params) +{ + struct mp_image_params nparams = *params; + nparams.imgfmt = image->imgfmt; + nparams.w = image->w; + nparams.h = image->h; + if (nparams.imgfmt != params->imgfmt) + mp_image_params_guess_csp(&nparams); + if (nparams.w != params->w || nparams.h != params->h) { + if (nparams.d_w && nparams.d_h) { + vf_rescale_dsize(&nparams.d_w, &nparams.d_h, + params->w, params->h, nparams.w, nparams.h); + } + } + mp_image_set_params(image, &nparams); +} + void mp_image_set_colorspace_details(struct mp_image *image, struct mp_csp_details *csp) { diff --git a/video/mp_image.h b/video/mp_image.h index e198c4e547..f7e7353475 100644 --- a/video/mp_image.h +++ b/video/mp_image.h @@ -153,6 +153,9 @@ void mp_image_params_from_image(struct mp_image_params *params, void mp_image_set_params(struct mp_image *image, const struct mp_image_params *params); +void mp_image_set_attributes(struct mp_image *image, + const struct mp_image_params *params); + struct AVFrame; void mp_image_copy_fields_from_av_frame(struct mp_image *dst, struct AVFrame *src); -- cgit v1.2.3 From 571e697a7c557d10bcc9130915c431829981d877 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 4 Nov 2013 00:00:18 +0100 Subject: vo_opengl: add infrastructure for hardware decoding OpenGL interop Most hardware decoding APIs provide some OpenGL interop. This allows using vo_opengl, without having to read the video data back from GPU. This requires adding a backend for each hardware decoding API. (Each backend is an entry in gl_hwdec_vaglx[].) The backends expose video data as a set of OpenGL textures. Add infrastructure to support this. The next commit will add support for VA-API. --- DOCS/man/en/vo.rst | 5 ++ video/decode/dec_video.h | 5 ++ video/decode/lavc.h | 2 + video/decode/vd_lavc.c | 7 +++ video/out/gl_common.c | 4 ++ video/out/gl_common.h | 38 +++++++++++++++ video/out/gl_video.c | 123 +++++++++++++++++++++++++++++++++++------------ video/out/gl_video.h | 4 +- video/out/vo_opengl.c | 73 +++++++++++++++++++++++++++- 9 files changed, 229 insertions(+), 32 deletions(-) diff --git a/DOCS/man/en/vo.rst b/DOCS/man/en/vo.rst index 4590ec33f2..fd14c6a128 100644 --- a/DOCS/man/en/vo.rst +++ b/DOCS/man/en/vo.rst @@ -241,6 +241,11 @@ Available video output drivers are: Some features are available with OpenGL 3 capable graphics drivers only (or if the necessary extensions are available). + Hardware decoding over OpenGL-interop is supported to some degree. Note + that in this mode, some corner case might not be gracefully handled, and + colorspace conversion and chroma upsampling is generally in the hand of + the hardware decoder APIs. + ``lscale=`` ``bilinear`` diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h index 549d208f81..03b72907ef 100644 --- a/video/decode/dec_video.h +++ b/video/decode/dec_video.h @@ -47,6 +47,11 @@ int vd_control(struct sh_video *sh_video, int cmd, void *arg); struct mp_hwdec_info { struct mp_vdpau_ctx *vdpau_ctx; struct mp_vaapi_ctx *vaapi_ctx; + // Can be used to lazily load a requested API. + // api_name is e.g. "vdpau" (like the fields above, without "_ctx") + // Can be NULL, is idempotent, caller checks _ctx fields for success/access. + void (*load_api)(struct mp_hwdec_info *info, const char *api_name); + void *load_api_ctx; }; #endif /* MPLAYER_DEC_VIDEO_H */ diff --git a/video/decode/lavc.h b/video/decode/lavc.h index 9e2533cbd5..af206bc82a 100644 --- a/video/decode/lavc.h +++ b/video/decode/lavc.h @@ -86,6 +86,8 @@ bool hwdec_check_codec_support(const char *decoder, const struct hwdec_profile_entry *table); int hwdec_get_max_refs(struct lavc_ctx *ctx); +void hwdec_request_api(struct mp_hwdec_info *info, const char *api_name); + // lavc_dr1.c int mp_codec_get_buffer(AVCodecContext *s, AVFrame *frame); void mp_codec_release_buffer(AVCodecContext *s, AVFrame *frame); diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index ebfd986a95..47bfd96e84 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -46,6 +46,7 @@ #include "video/img_format.h" #include "video/mp_image_pool.h" #include "video/filter/vf.h" +#include "video/decode/dec_video.h" #include "demux/stheader.h" #include "demux/demux_packet.h" #include "osdep/numcores.h" @@ -196,6 +197,12 @@ int hwdec_get_max_refs(struct lavc_ctx *ctx) return ctx->avctx->codec_id == AV_CODEC_ID_H264 ? 16 : 2; } +void hwdec_request_api(struct mp_hwdec_info *info, const char *api_name) +{ + if (info && info->load_api) + info->load_api(info, api_name); +} + static int hwdec_probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, const char *decoder, const char **hw_decoder) { diff --git a/video/out/gl_common.c b/video/out/gl_common.c index 10e806dc8d..9482f9de55 100644 --- a/video/out/gl_common.c +++ b/video/out/gl_common.c @@ -1008,3 +1008,7 @@ void mp_log_source(struct mp_log *log, int lev, const char *src) src = next; } } + +const struct gl_hwdec_driver *mpgl_hwdec_drivers[] = { + NULL +}; diff --git a/video/out/gl_common.h b/video/out/gl_common.h index ecff698ac8..d8d07de06a 100644 --- a/video/out/gl_common.h +++ b/video/out/gl_common.h @@ -164,6 +164,44 @@ void mpgl_set_backend_w32(MPGLContext *ctx); void mpgl_set_backend_x11(MPGLContext *ctx); void mpgl_set_backend_wayland(MPGLContext *ctx); +struct mp_hwdec_info; + +struct gl_hwdec { + const struct gl_hwdec_driver *driver; + struct mp_log *log; + struct MPGLContext *mpgl; + struct mp_hwdec_info *info; + // For free use by hwdec driver + void *priv; + // hwdec backends must set this to an IMGFMT_ that has an equivalent + // internal representation in gl_video.c as the hardware texture. + // It's used to build the rendering chain, and also as screenshot format. + int converted_imgfmt; +}; + +struct gl_hwdec_driver { + // Same name as used by mp_hwdec_info->load_api() + const char *api_name; + // Test whether the given IMGFMT_ is supported. + bool (*query_format)(int imgfmt); + // Create the hwdec device. It must fill in hw->info, if applicable. + int (*create)(struct gl_hwdec *hw); + // Prepare for rendering video. (E.g. create textures.) + // Called on initialization, and every time the video size changes. + int (*reinit)(struct gl_hwdec *hw, int w, int h); + // Return textures that contain the given hw_image. + // Note that the caller keeps a reference to hw_image until unbind_image + // is called, so the callee doesn't need to do that. + int (*load_image)(struct gl_hwdec *hw, struct mp_image *hw_image, + GLuint *out_textures); + // Undo load_image(). The user of load_image() calls this when the textures + // are not needed anymore. + void (*unload_image)(struct gl_hwdec *hw); + void (*destroy)(struct gl_hwdec *hw); +}; + +extern const struct gl_hwdec_driver *mpgl_hwdec_drivers[]; + void *mp_getdladdr(const char *s); void mpgl_load_functions(GL *gl, void *(*getProcAddress)(const GLubyte *), diff --git a/video/out/gl_video.c b/video/out/gl_video.c index d4b4b507c3..b376fead36 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -114,6 +114,7 @@ struct texplane { struct video_image { struct texplane planes[4]; bool image_flipped; + struct mp_image *hwimage; // if hw decoding is active }; struct scaler { @@ -202,6 +203,9 @@ struct gl_video { int last_dither_matrix_size; float *last_dither_matrix; + struct gl_hwdec *hwdec; + bool hwdec_active; + void *scratch; }; @@ -1129,7 +1133,7 @@ static void reinit_rendering(struct gl_video *p) uninit_rendering(p); - if (!p->image.planes[0].gl_texture) + if (!p->image_format) return; for (int n = 0; n < 2; n++) @@ -1196,17 +1200,41 @@ void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d) debug_check_gl(p, "after 3d lut creation"); } -static void set_image_textures(struct gl_video *p, struct video_image *vimg) +static void set_image_textures(struct gl_video *p, struct video_image *vimg, + GLuint imgtex[4]) { GL *gl = p->gl; + GLuint dummy[4]; + if (!imgtex) + imgtex = dummy; - for (int n = 0; n < p->plane_count; n++) { - struct texplane *plane = &vimg->planes[n]; + if (p->hwdec_active) { + assert(vimg->hwimage); + p->hwdec->driver->load_image(p->hwdec, vimg->hwimage, imgtex); + } else { + for (int n = 0; n < p->plane_count; n++) + imgtex[n] = vimg->planes[n].gl_texture; + } + for (int n = 0; n < 4; n++) { gl->ActiveTexture(GL_TEXTURE0 + n); - gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture); + gl->BindTexture(GL_TEXTURE_2D, imgtex[n]); + } + gl->ActiveTexture(GL_TEXTURE0); +} + +static void unset_image_textures(struct gl_video *p) +{ + GL *gl = p->gl; + + for (int n = 0; n < 4; n++) { + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->BindTexture(GL_TEXTURE_2D, 0); } gl->ActiveTexture(GL_TEXTURE0); + + if (p->hwdec_active) + p->hwdec->driver->unload_image(p->hwdec); } static void init_video(struct gl_video *p) @@ -1243,21 +1271,27 @@ static void init_video(struct gl_video *p) plane->w = full_w >> p->image_desc.xs[n]; plane->h = full_h >> p->image_desc.ys[n]; - texture_size(p, plane->w, plane->h, - &plane->tex_w, &plane->tex_h); + if (p->hwdec_active) { + // We expect hwdec backends to allocate exact size + plane->tex_w = plane->w; + plane->tex_h = plane->h; + } else { + texture_size(p, plane->w, plane->h, + &plane->tex_w, &plane->tex_h); - MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", - n, plane->tex_w, plane->tex_h); + gl->ActiveTexture(GL_TEXTURE0 + n); + gl->GenTextures(1, &plane->gl_texture); + gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture); - gl->ActiveTexture(GL_TEXTURE0 + n); - gl->GenTextures(1, &plane->gl_texture); - gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture); + gl->TexImage2D(GL_TEXTURE_2D, 0, plane->gl_internal_format, + plane->tex_w, plane->tex_h, 0, + plane->gl_format, plane->gl_type, NULL); - gl->TexImage2D(GL_TEXTURE_2D, 0, plane->gl_internal_format, - plane->tex_w, plane->tex_h, 0, - plane->gl_format, plane->gl_type, NULL); + default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR); + } - default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR); + MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", + n, plane->tex_w, plane->tex_h); } gl->ActiveTexture(GL_TEXTURE0); @@ -1266,6 +1300,11 @@ static void init_video(struct gl_video *p) debug_check_gl(p, "after video texture creation"); + if (p->hwdec_active) { + if (p->hwdec->driver->reinit(p->hwdec, p->image_w, p->image_h) < 0) + MP_ERR(p, "Initializing hardware ddecoding video texture failed.\n"); + } + reinit_rendering(p); } @@ -1287,6 +1326,7 @@ static void uninit_video(struct gl_video *p) plane->buffer_ptr = NULL; plane->buffer_size = 0; } + mp_image_unrefp(&vimg->hwimage); fbotex_uninit(p, &p->indirect_fbo); fbotex_uninit(p, &p->scale_sep_fbo); @@ -1373,14 +1413,15 @@ void gl_video_render_frame(struct gl_video *p) // Order of processing: // [indirect -> [scale_sep ->]] final - set_image_textures(p, vimg); + GLuint imgtex[4] = {0}; + set_image_textures(p, vimg, imgtex); struct fbotex chain = { .vp_w = p->image_w, .vp_h = p->image_h, .tex_w = p->texture_w, .tex_h = p->texture_h, - .texture = vimg->planes[0].gl_texture, + .texture = imgtex[0], }; handle_pass(p, &chain, &p->indirect_fbo, p->indirect_program); @@ -1441,6 +1482,8 @@ void gl_video_render_frame(struct gl_video *p) gl->UseProgram(0); + unset_image_textures(p); + p->frames_rendered++; debug_check_gl(p, "after video rendering"); @@ -1552,16 +1595,21 @@ static bool get_image(struct gl_video *p, struct mp_image *mpi) void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) { GL *gl = p->gl; - int n; - - assert(mpi->num_planes == p->plane_count); struct video_image *vimg = &p->image; + if (p->hwdec_active) { + mp_image_setrefp(&vimg->hwimage, mpi); + p->have_image = true; + return; + } + + assert(mpi->num_planes == p->plane_count); + mp_image_t mpi2 = *mpi; bool pbo = false; if (!vimg->planes[0].buffer_ptr && get_image(p, &mpi2)) { - for (n = 0; n < p->plane_count; n++) { + for (int n = 0; n < p->plane_count; n++) { int line_bytes = mpi->plane_w[n] * p->image_desc.bytes[n]; memcpy_pic(mpi2.planes[n], mpi->planes[n], line_bytes, mpi->plane_h[n], mpi2.stride[n], mpi->stride[n]); @@ -1570,7 +1618,7 @@ void gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) pbo = true; } vimg->image_flipped = mpi->stride[0] < 0; - for (n = 0; n < p->plane_count; n++) { + for (int n = 0; n < p->plane_count; n++) { struct texplane *plane = &vimg->planes[n]; void *plane_ptr = mpi->planes[n]; if (pbo) { @@ -1598,10 +1646,11 @@ struct mp_image *gl_video_download_image(struct gl_video *p) struct video_image *vimg = &p->image; - if (!p->have_image || !vimg->planes[0].gl_texture) + if (!p->have_image) return NULL; - assert(p->image_format == p->image_params.imgfmt); + set_image_textures(p, vimg, NULL); + assert(p->texture_w >= p->image_params.w); assert(p->texture_h >= p->image_params.h); @@ -1611,12 +1660,12 @@ struct mp_image *gl_video_download_image(struct gl_video *p) for (int n = 0; n < p->plane_count; n++) { struct texplane *plane = &vimg->planes[n]; gl->ActiveTexture(GL_TEXTURE0 + n); - gl->BindTexture(GL_TEXTURE_2D, plane->gl_texture); glDownloadTex(gl, GL_TEXTURE_2D, plane->gl_format, plane->gl_type, image->planes[n], image->stride[n]); } - gl->ActiveTexture(GL_TEXTURE0); - mp_image_set_params(image, &p->image_params); + mp_image_set_attributes(image, &p->image_params); + + unset_image_textures(p); return image; } @@ -1884,6 +1933,12 @@ static bool init_format(int fmt, struct gl_video *init) if (!init) init = &dummy; + init->hwdec_active = false; + if (init->hwdec && init->hwdec->driver->query_format(fmt)) { + fmt = init->hwdec->converted_imgfmt; + init->hwdec_active = true; + } + struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(fmt); if (!desc.id) return false; @@ -1985,9 +2040,10 @@ static bool init_format(int fmt, struct gl_video *init) return true; } -bool gl_video_check_format(int mp_format) +bool gl_video_check_format(struct gl_video *p, int mp_format) { - return init_format(mp_format, NULL); + struct gl_video tmp = *p; + return init_format(mp_format, &tmp); } void gl_video_config(struct gl_video *p, struct mp_image_params *params) @@ -2012,6 +2068,7 @@ void gl_video_config(struct gl_video *p, struct mp_image_params *params) p->colorspace = csp; p->have_image = false; + mp_image_unrefp(&p->image.hwimage); } void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b) @@ -2132,3 +2189,9 @@ void gl_video_resize_redraw(struct gl_video *p, int w, int h) gl_video_render_frame(p); mpgl_osd_redraw_cb(p->osd, draw_osd_cb, p); } + +void gl_video_set_hwdec(struct gl_video *p, struct gl_hwdec *hwdec) +{ + p->hwdec = hwdec; + mp_image_unrefp(&p->image.hwimage); +} diff --git a/video/out/gl_video.h b/video/out/gl_video.h index dcec9f3888..cab5f0f077 100644 --- a/video/out/gl_video.h +++ b/video/out/gl_video.h @@ -56,6 +56,7 @@ struct gl_video; struct gl_video *gl_video_init(GL *gl, struct mp_log *log); void gl_video_uninit(struct gl_video *p); void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts); +bool gl_video_check_format(struct gl_video *p, int mp_format); void gl_video_config(struct gl_video *p, struct mp_image_params *params); void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b); void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d); @@ -73,6 +74,7 @@ bool gl_video_get_equalizer(struct gl_video *p, const char *name, int *val); void gl_video_set_debug(struct gl_video *p, bool enable); void gl_video_resize_redraw(struct gl_video *p, int w, int h); -bool gl_video_check_format(int mp_format); +struct gl_hwdec; +void gl_video_set_hwdec(struct gl_video *p, struct gl_hwdec *hwdec); #endif diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c index cf1b3b3400..8572f587a4 100644 --- a/video/out/vo_opengl.c +++ b/video/out/vo_opengl.c @@ -48,6 +48,7 @@ #include "gl_osd.h" #include "filter_kernels.h" #include "video/memcpy_pic.h" +#include "video/decode/dec_video.h" #include "gl_video.h" #include "gl_lcms.h" @@ -58,6 +59,8 @@ struct gl_priv { struct gl_video *renderer; + struct gl_hwdec *hwdec; + // Options struct gl_video_opts *renderer_opts; struct mp_icc_opts *icc_opts; @@ -134,8 +137,9 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) static int query_format(struct vo *vo, uint32_t format) { + struct gl_priv *p = vo->priv; int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP; - if (!gl_video_check_format(format)) + if (!gl_video_check_format(p->renderer, format)) return 0; return caps; } @@ -191,6 +195,68 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) return 0; } + +static void load_hwdec_driver(struct gl_priv *p, + const struct gl_hwdec_driver *drv) +{ + assert(!p->hwdec); + struct gl_hwdec *hwdec = talloc(NULL, struct gl_hwdec); + *hwdec = (struct gl_hwdec) { + .driver = drv, + .log = mp_log_new(hwdec, p->vo->log, drv->api_name), + .mpgl = p->glctx, + .info = talloc_zero(hwdec, struct mp_hwdec_info), + }; + mpgl_lock(p->glctx); + if (hwdec->driver->create(hwdec) < 0) { + mpgl_unlock(p->glctx); + talloc_free(hwdec); + MP_ERR(p->vo, "Couldn't load hwdec driver '%s'\n", drv->api_name); + return; + } + p->hwdec = hwdec; + gl_video_set_hwdec(p->renderer, p->hwdec); + mpgl_unlock(p->glctx); +} + +static void request_hwdec_api(struct mp_hwdec_info *info, const char *api_name) +{ + struct gl_priv *p = info->load_api_ctx; + // Load at most one hwdec API + if (p->hwdec) + return; + for (int n = 0; mpgl_hwdec_drivers[n]; n++) { + const struct gl_hwdec_driver *drv = mpgl_hwdec_drivers[n]; + if (api_name && strcmp(drv->api_name, api_name) == 0) { + load_hwdec_driver(p, drv); + if (p->hwdec) { + *info = *p->hwdec->info; + return; + } + } + } +} + +static void get_hwdec_info(struct gl_priv *p, struct mp_hwdec_info *info) +{ + info->load_api = request_hwdec_api; + info->load_api_ctx = p; + if (p->hwdec) + *info = *p->hwdec->info; +} + +static void unload_hwdec_driver(struct gl_priv *p) +{ + if (p->hwdec) { + mpgl_lock(p->glctx); + gl_video_set_hwdec(p->renderer, NULL); + p->hwdec->driver->destroy(p->hwdec); + talloc_free(p->hwdec); + p->hwdec = NULL; + mpgl_unlock(p->glctx); + } +} + static bool reparse_cmdline(struct gl_priv *p, char *args) { struct m_config *cfg = NULL; @@ -263,6 +329,10 @@ static int control(struct vo *vo, uint32_t request, void *data) mpgl_unlock(p->glctx); return true; } + case VOCTRL_GET_HWDEC_INFO: { + get_hwdec_info(p, data); + return true; + } case VOCTRL_REDRAW_FRAME: mpgl_lock(p->glctx); gl_video_render_frame(p->renderer); @@ -291,6 +361,7 @@ static void uninit(struct vo *vo) struct gl_priv *p = vo->priv; if (p->glctx) { + unload_hwdec_driver(p); if (p->renderer) gl_video_uninit(p->renderer); mpgl_uninit(p->glctx); -- cgit v1.2.3 From 2d58fb3b8e7e87a939220f50a51294f8efdc51e1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 4 Nov 2013 00:02:21 +0100 Subject: vo_opengl: add support for VA-API OpenGL interop VA-API's OpenGL/GLX interop is pretty bad and perhaps slow (renders a X11 pixmap into a FBO, and has to go over X11, probably involves one or more copies), and this code serves more as an example, rather than for serious use. On the other hand, this might be work much better than vo_vaapi, even if slightly slower. --- DOCS/man/en/options.rst | 2 +- Makefile | 1 + configure | 12 +++- video/decode/vaapi.c | 1 + video/out/gl_common.c | 5 ++ video/out/gl_common.h | 1 + video/out/gl_hwdec_vaglx.c | 140 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 video/out/gl_hwdec_vaglx.c diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index 18542b2e37..f28280f73f 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -1118,7 +1118,7 @@ OPTIONS :no: always use software decoding (default) :auto: see below :vdpau: requires ``--vo=vdpau`` (Linux only) - :vaapi: requires ``--vo=vaapi`` (Linux with Intel GPUs only) + :vaapi: requires ``--vo=opengl`` or ``--vo=vaapi`` (Linux with Intel GPUs only) :vaapi-copy: copies video back into system RAM (Linux with Intel GPUs only) :vda: requires ``--vo=corevideo`` (OSX only) :crystalhd: Broadcom Crystal HD diff --git a/Makefile b/Makefile index bed21f20e5..e1cacc0ea5 100644 --- a/Makefile +++ b/Makefile @@ -121,6 +121,7 @@ SOURCES-$(VAAPI) += video/out/vo_vaapi.c \ video/decode/vaapi.c \ video/vaapi.c SOURCES-$(VAAPI_VPP) += video/filter/vf_vavpp.c +SOURCES-$(VAAPI_GLX) += video/out/gl_hwdec_vaglx.c SOURCES-$(X11) += video/out/vo_x11.c video/out/x11_common.c SOURCES-$(XV) += video/out/vo_xv.c diff --git a/configure b/configure index a15e08bca5..35be7c75f5 100755 --- a/configure +++ b/configure @@ -1860,6 +1860,8 @@ echores "$_vdpau" echocheck "VAAPI" _vaapi_vpp=no def_vaapi_vpp='#define CONFIG_VAAPI_VPP 0' +_vaapi_glx=no +def_vaapi_glx='#define CONFIG_VAAPI_GLX 0' if test "$_vaapi" = auto && test "$_x11" = yes ; then _vaapi=no if test "$_dl" = yes ; then @@ -1881,7 +1883,13 @@ if test "$_vaapi" = yes ; then _vaapi_vpp=yes def_vaapi_vpp='#define CONFIG_VAAPI_VPP 1' fi - echores "$_vaapi_vpp" + echores "$_vaapi_glx" + echocheck "VAAPI GLX" + if pkg_config_add 'libva-glx >= 0.32.0' ; then + _vaapi_glx=yes + def_vaapi_glx='#define CONFIG_VAAPI_GLX 1' + fi + echores "$_vaapi_glx" fi @@ -3297,6 +3305,7 @@ VDA = $_vda VDA_REFCOUNTING = $_vda_refcounting VAAPI = $_vaapi VAAPI_VPP = $_vaapi_vpp +VAAPI_GLX = $_vaapi_glx WIN32 = $_win32 X11 = $_x11 WAYLAND = $_wayland @@ -3475,6 +3484,7 @@ $def_vda $def_vda_refcounting $def_vaapi $def_vaapi_vpp +$def_vaapi_glx $def_vm $def_x11 $def_wayland diff --git a/video/decode/vaapi.c b/video/decode/vaapi.c index bb2a6c1049..4603a3c5e1 100644 --- a/video/decode/vaapi.c +++ b/video/decode/vaapi.c @@ -421,6 +421,7 @@ static int init(struct lavc_ctx *ctx) static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, const char *decoder) { + hwdec_request_api(info, "vaapi"); if (!info || !info->vaapi_ctx) return HWDEC_ERR_NO_CTX; if (!hwdec_check_codec_support(decoder, profiles)) diff --git a/video/out/gl_common.c b/video/out/gl_common.c index 9482f9de55..3216b0037e 100644 --- a/video/out/gl_common.c +++ b/video/out/gl_common.c @@ -1009,6 +1009,11 @@ void mp_log_source(struct mp_log *log, int lev, const char *src) } } +extern const struct gl_hwdec_driver gl_hwdec_vaglx; + const struct gl_hwdec_driver *mpgl_hwdec_drivers[] = { +#if CONFIG_VAAPI_GLX + &gl_hwdec_vaglx, +#endif NULL }; diff --git a/video/out/gl_common.h b/video/out/gl_common.h index d8d07de06a..8d318a7c1f 100644 --- a/video/out/gl_common.h +++ b/video/out/gl_common.h @@ -185,6 +185,7 @@ struct gl_hwdec_driver { // Test whether the given IMGFMT_ is supported. bool (*query_format)(int imgfmt); // Create the hwdec device. It must fill in hw->info, if applicable. + // This also must set hw->converted_imgfmt. int (*create)(struct gl_hwdec *hw); // Prepare for rendering video. (E.g. create textures.) // Called on initialization, and every time the video size changes. diff --git a/video/out/gl_hwdec_vaglx.c b/video/out/gl_hwdec_vaglx.c new file mode 100644 index 0000000000..9e1030fe49 --- /dev/null +++ b/video/out/gl_hwdec_vaglx.c @@ -0,0 +1,140 @@ +/* + * This file is part of mpv. + * + * Parts based on the MPlayer VA-API patch (see vo_vaapi.c). + * + * 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 . + */ + +#include +#include + +#include +#include + +#include "x11_common.h" +#include "gl_common.h" +#include "video/vaapi.h" +#include "video/decode/dec_video.h" + +struct priv { + struct mp_vaapi_ctx *ctx; + VADisplay *display; + GLuint gl_texture; + void *vaglx_surface; +}; + +static bool query_format(int imgfmt) +{ + return imgfmt == IMGFMT_VAAPI; +} + +static void destroy_texture(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + VAStatus status; + + if (p->vaglx_surface) { + status = vaDestroySurfaceGLX(p->display, p->vaglx_surface); + check_va_status(status, "vaDestroySurfaceGLX()"); + p->vaglx_surface = NULL; + } + + glDeleteTextures(1, &p->gl_texture); + p->gl_texture = 0; +} + +static void destroy(struct gl_hwdec *hw) +{ + struct priv *p = hw->priv; + destroy_texture(hw); + va_destroy(p->ctx); +} + +static int create(struct gl_hwdec *hw) +{ + if (hw->info->vaapi_ctx) + return -1; + if (!hw->mpgl->vo->x11 || !glXGetCurrentContext()) + return -1; + struct priv *p = talloc_zero(hw, struct priv); + hw->priv = p; + p->display = vaGetDisplayGLX(hw->mpgl->vo->x11->display); + if (!p->display) + return -1; + p->ctx = va_initialize(p->display); + if (!p->ctx) { + vaTerminate(p->display); + return -1; + } + hw->info->vaapi_ctx = p->ctx; + hw->converted_imgfmt = IMGFMT_RGBA; + return 0; +} + +static int reinit(struct gl_hwdec *hw, int w, int h) +{ + struct priv *p = hw->priv; + GL *gl = hw->mpgl->gl; + VAStatus status; + + destroy_texture(hw); + + gl->GenTextures(1, &p->gl_texture); + gl->BindTexture(GL_TEXTURE_2D, p->gl_texture); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + gl->BindTexture(GL_TEXTURE_2D, 0); + + status = vaCreateSurfaceGLX(p->display, GL_TEXTURE_2D, + p->gl_texture, &p->vaglx_surface); + return check_va_status(status, "vaCreateSurfaceGLX()") ? 0 : -1; +} + +static int load_image(struct gl_hwdec *hw, struct mp_image *hw_image, + GLuint *out_textures) +{ + struct priv *p = hw->priv; + VAStatus status; + + if (!p->vaglx_surface) + return -1; + + status = vaCopySurfaceGLX(p->display, p->vaglx_surface, + va_surface_id_in_mp_image(hw_image), + va_get_colorspace_flag(hw_image->colorspace)); + if (!check_va_status(status, "vaCopySurfaceGLX()")) + return -1; + + out_textures[0] = p->gl_texture; + return 0; +} + +static void unload_image(struct gl_hwdec *hw) +{ +} + +const struct gl_hwdec_driver gl_hwdec_vaglx = { + .api_name = "vaapi", + .query_format = query_format, + .create = create, + .reinit = reinit, + .load_image = load_image, + .unload_image = unload_image, + .destroy = destroy, +}; -- cgit v1.2.3 From 25affdcc886ce010995804553396d81d90a321d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Forysiuk?= Date: Sun, 3 Nov 2013 23:28:13 +0100 Subject: Fix -Wshadow warning about seek function in playloop.c mpvcore/player/playloop.c: In function 'seek': mpvcore/player/playloop.c:209:54: warning: declaration of 'seek' shadows a global declaration [-Wshadow] mpvcore/player/playloop.c:209:12: warning: shadowed declaration is here [-Wshadow] mpvcore/player/playloop.c: In function 'queue_seek': mpvcore/player/playloop.c:360:25: warning: declaration of 'seek' shadows a global declaration [-Wshadow] mpvcore/player/playloop.c:209:12: warning: shadowed declaration is here [-Wshadow] Signed-off-by: wm4 --- mpvcore/player/playloop.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mpvcore/player/playloop.c b/mpvcore/player/playloop.c index 90525a5ca2..c2263ab57e 100644 --- a/mpvcore/player/playloop.c +++ b/mpvcore/player/playloop.c @@ -206,8 +206,8 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac) } // return -1 if seek failed (non-seekable stream?), 0 otherwise -static int seek(MPContext *mpctx, struct seek_params seek, - bool timeline_fallthrough) +static int mp_seek(MPContext *mpctx, struct seek_params seek, + bool timeline_fallthrough) { struct MPOpts *opts = mpctx->opts; uint64_t prev_seek_ts = mpctx->vo_pts_history_seek_ts; @@ -392,7 +392,7 @@ void queue_seek(struct MPContext *mpctx, enum seek_type type, double amount, void execute_queued_seek(struct MPContext *mpctx) { if (mpctx->seek.type) { - seek(mpctx, mpctx->seek, false); + mp_seek(mpctx, mpctx->seek, false); mpctx->seek = (struct seek_params){0}; } } @@ -818,10 +818,10 @@ static void handle_backstep(struct MPContext *mpctx) // The whole point is getting frames _before_ that PTS, // so apply an arbitrary offset. (In theory the offset // has to be large enough to reach the previous frame.) - seek(mpctx, (struct seek_params){ - .type = MPSEEK_ABSOLUTE, - .amount = current_pts - 1.0, - }, false); + mp_seek(mpctx, (struct seek_params){ + .type = MPSEEK_ABSOLUTE, + .amount = current_pts - 1.0, + }, false); // Don't leave hr-seek mode. If all goes right, hr-seek // mode is cancelled as soon as the frame before // current_pts is found during hr-seeking. @@ -1190,10 +1190,10 @@ void run_playloop(struct MPContext *mpctx) && (opts->gapless_audio || buffered_audio < 0.05) && (!mpctx->paused || was_restart)) { if (end_is_chapter) { - seek(mpctx, (struct seek_params){ - .type = MPSEEK_ABSOLUTE, - .amount = mpctx->timeline[mpctx->timeline_part+1].start - }, true); + mp_seek(mpctx, (struct seek_params){ + .type = MPSEEK_ABSOLUTE, + .amount = mpctx->timeline[mpctx->timeline_part+1].start + }, true); } else mpctx->stop_play = AT_END_OF_FILE; sleeptime = 0; -- cgit v1.2.3