Merge branch 'master' into have_configure

Conflicts:
	configure
This commit is contained in:
wm4 2013-11-04 00:43:06 +01:00
commit f7b2d644ef
28 changed files with 480 additions and 96 deletions

View File

@ -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

View File

@ -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=<filter>``
``bilinear``

View File

@ -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

View File

@ -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

12
configure vendored
View File

@ -1863,6 +1863,8 @@ echores "$_vdpau"
echocheck "VAAPI"
_vaapi_vpp=no
def_vaapi_vpp='#define HAVE_VAAPI_VPP 0'
_vaapi_glx=no
def_vaapi_glx='#define HAVE_VAAPI_GLX 0'
if test "$_vaapi" = auto && test "$_x11" = yes ; then
_vaapi=no
if test "$_dl" = yes ; then
@ -1886,7 +1888,13 @@ if test "$_vaapi" = yes ; then
_vaapi_vpp=yes
def_vaapi_vpp='#define HAVE_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 HAVE_VAAPI_GLX 1'
fi
echores "$_vaapi_glx"
fi
@ -3321,6 +3329,7 @@ VDA = $_vda
VDA_REFCOUNTING = $_vda_refcounting
VAAPI = $_vaapi
VAAPI_VPP = $_vaapi_vpp
VAAPI_GLX = $_vaapi_glx
WIN32 = $_win32
X11 = $_x11
WAYLAND = $_wayland
@ -3501,6 +3510,7 @@ $def_vda
$def_vda_refcounting
$def_vaapi
$def_vaapi_vpp
$def_vaapi_glx
$def_vaapi_hwaccel
$def_vm
$def_x11

View File

@ -533,9 +533,8 @@ 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,
.seekable = (stream->flags & MP_STREAM_SEEK) == MP_STREAM_SEEK &&
stream->end_pos > 0,
.accurate_seek = true,
.filepos = -1,
.opts = opts,
@ -566,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;
}
@ -634,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;
}

View File

@ -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

View File

@ -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;
}
@ -857,12 +858,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 +924,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)

View File

@ -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;
}

View File

@ -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;
@ -214,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;

View File

@ -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;
@ -1844,14 +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->movi_start = s->start_pos;
demuxer->movi_end = s->end_pos;
demuxer->seekable = 1;
}
return 0;
}
@ -2713,7 +2705,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 +2721,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)

View File

@ -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);

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;
@ -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;
@ -387,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};
}
}
@ -452,11 +457,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)
@ -812,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.
@ -1184,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;

View File

@ -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,6 +387,8 @@ static int stream_reconnect(stream_t *s)
#define RECONNECT_SLEEP_MS 1000
if (!s->streaming)
return 0;
if (!(s->flags & MP_STREAM_SEEK_FW))
return 0;
int64_t pos = s->pos;
for (int retry = 0; retry < MAX_RECONNECT_RETRIES; retry++) {
mp_msg(MSGT_STREAM, MSGL_WARN,
@ -601,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;
}
@ -628,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;
}
@ -640,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;
@ -709,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)

View File

@ -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 */

View File

@ -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);

View File

@ -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))

View File

@ -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"
@ -195,6 +196,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)
{

View File

@ -36,6 +36,8 @@
#include "memcpy_pic.h"
#include "fmt-conversion.h"
#include "video/filter/vf.h"
#if HAVE_PTHREADS
#include <pthread.h>
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)
{

View File

@ -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);

View File

@ -1008,3 +1008,12 @@ void mp_log_source(struct mp_log *log, int lev, const char *src)
src = next;
}
}
extern const struct gl_hwdec_driver gl_hwdec_vaglx;
const struct gl_hwdec_driver *mpgl_hwdec_drivers[] = {
#if HAVE_VAAPI_GLX
&gl_hwdec_vaglx,
#endif
NULL
};

View File

@ -164,6 +164,45 @@ 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.
// 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.
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 *),

140
video/out/gl_hwdec_vaglx.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <assert.h>
#include <GL/glx.h>
#include <va/va_glx.h>
#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,
};

View File

@ -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;
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, imgtex[n]);
}
gl->ActiveTexture(GL_TEXTURE0);
}
static void unset_image_textures(struct gl_video *p)
{
GL *gl = p->gl;
for (int n = 0; n < p->plane_count; n++) {
struct texplane *plane = &vimg->planes[n];
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, 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);
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);
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 + 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);
default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
}
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);
}

View File

@ -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

View File

@ -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);