From b9f8239b38a9100e2f30b8bb54967c436dbb073a Mon Sep 17 00:00:00 2001 From: Petri Hintukainen Date: Tue, 29 Oct 2013 13:46:47 +0200 Subject: Added VP8/VP9 decoder using libvpx --- src/video_dec/Makefile.am | 9 ++ src/video_dec/libvpx.c | 364 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 src/video_dec/libvpx.c (limited to 'src') diff --git a/src/video_dec/Makefile.am b/src/video_dec/Makefile.am index 10c6b18eb..c68c9192a 100644 --- a/src/video_dec/Makefile.am +++ b/src/video_dec/Makefile.am @@ -24,10 +24,15 @@ if ENABLE_LIBJPEG libjpeg_module = xineplug_decode_libjpeg.la endif +if ENABLE_VPX +libvpx_module = xineplug_decode_libvpx.la +endif + xineplug_LTLIBRARIES = $(image_module) \ $(gdkpixbuf_module) \ $(libjpeg_module) \ $(theora_module) \ + $(libvpx_module) \ xineplug_decode_bitplane.la \ xineplug_decode_rgb.la \ xineplug_decode_yuv.la @@ -52,3 +57,7 @@ xineplug_decode_gdk_pixbuf_la_CFLAGS = $(AM_CFLAGS) $(GDK_PIXBUF_CFLAGS) xineplug_decode_libjpeg_la_SOURCES = libjpeg.c xineplug_decode_libjpeg_la_LIBADD = $(XINE_LIB) $(JPEG_LIBS) xineplug_decode_libjpeg_la_CFLAGS = $(AM_CFLAGS) $(JPEG_CFLAGS) + +xineplug_decode_libvpx_la_SOURCES = libvpx.c +xineplug_decode_libvpx_la_LIBADD = $(XINE_LIB) $(VPX_LIBS) +xineplug_decode_libvpx_la_CFLAGS = $(AM_CFLAGS) $(VPX_CFLAGS) diff --git a/src/video_dec/libvpx.c b/src/video_dec/libvpx.c new file mode 100644 index 000000000..6b67675df --- /dev/null +++ b/src/video_dec/libvpx.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2013 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * libvpx decoder wrapped by Petri Hintukainen + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include + +#include +#include +#include +#include + +typedef struct { + video_decoder_class_t decoder_class; + uint32_t buffer_type; +} vpx_class_t; + +typedef struct vpx_decoder_s { + video_decoder_t video_decoder; /* parent video decoder structure */ + + vpx_class_t *class; + xine_stream_t *stream; + + int64_t pts; + struct vpx_codec_ctx ctx; + int decoder_ok; /* current decoder status */ + + unsigned char *buf; /* the accumulated buffer data */ + int bufsize; /* the maximum size of buf */ + int size; /* the current size of buf */ + + int width; /* the width of a video frame */ + int height; /* the height of a video frame */ + double ratio; /* the width to height ratio */ + +} vpx_decoder_t; + +/************************************************************************** + * xine video plugin functions + *************************************************************************/ + +static void vpx_handle_header(vpx_decoder_t *this, buf_element_t *buf) +{ + xine_bmiheader *bih; + + (this->stream->video_out->open) (this->stream->video_out, this->stream); + + bih = (xine_bmiheader *) buf->content; + this->width = (bih->biWidth + 1) & ~1; + this->height = (bih->biHeight + 1) & ~1; + + if (buf->decoder_flags & BUF_FLAG_ASPECT) + this->ratio = (double)buf->decoder_info[1] / (double)buf->decoder_info[2]; + else + this->ratio = (double)this->width / (double)this->height; + + free (this->buf); + this->buf = NULL; + this->bufsize = 0; + this->size = 0; + + this->decoder_ok = 1; + + switch (buf->type) { + case BUF_VIDEO_VP8: + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "VP8"); + break; + case BUF_VIDEO_VP9: + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "VP9"); + break; + } + + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_RATIO, this->ratio*10000); +} + +static void vpx_decode_data (video_decoder_t *this_gen, buf_element_t *buf) +{ + vpx_decoder_t *this = (vpx_decoder_t *) this_gen; + + if (buf->decoder_flags & BUF_FLAG_PREVIEW) { + return; + } + + if (buf->decoder_flags & BUF_FLAG_STDHEADER) { + vpx_handle_header(this, buf); + return; + } + + if (!this->decoder_ok || buf->decoder_flags & BUF_FLAG_SPECIAL) { + return; + } + + /* collect data */ + if (this->size + buf->size > this->bufsize) { + this->bufsize = this->size + 2 * buf->size; + this->buf = realloc (this->buf, this->bufsize); + } + xine_fast_memcpy (&this->buf[this->size], buf->content, buf->size); + this->size += buf->size; + + /* save pts */ + if (buf->pts > 0) { + this->pts = buf->pts; + } + + if (!(buf->decoder_flags & BUF_FLAG_FRAME_END)) { + return; + } + + /* decode */ + + struct vpx_codec_ctx *ctx = &this->ctx; + vpx_codec_err_t err; + vo_frame_t *img; + int64_t pts, *p_pts; + + p_pts = malloc(sizeof(*p_pts)); + *p_pts = this->pts; + err = vpx_codec_decode(ctx, this->buf, this->size, p_pts, 0); + + this->size = 0; + + if (err != VPX_CODEC_OK) { + const char *error = vpx_codec_error(ctx); + const char *detail = vpx_codec_error_detail(ctx); + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE": Failed to decode frame: %s (%s)\n", + error, detail ? detail : ""); + free(p_pts); + return; + } + + const void *iter = NULL; + struct vpx_image *vpx_img = vpx_codec_get_frame(ctx, &iter); + if (!vpx_img) + return; + + p_pts = vpx_img->user_priv; + vpx_img->user_priv = NULL; + pts = *p_pts; + free(p_pts); + + if (vpx_img->fmt != VPX_IMG_FMT_I420) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE": Unsupported color space %d\n", vpx_img->fmt); + return; + } + + img = this->stream->video_out->get_frame (this->stream->video_out, + this->width, this->height, + this->ratio, XINE_IMGFMT_YV12, VO_BOTH_FIELDS); + + yv12_to_yv12( + /* Y */ + vpx_img->planes[0], vpx_img->stride[0], + img->base[0], img->pitches[0], + /* U */ + vpx_img->planes[1], vpx_img->stride[1], + img->base[1], img->pitches[1], + /* V */ + vpx_img->planes[2], vpx_img->stride[2], + img->base[2], img->pitches[2], + /* width x height */ + this->width, this->height); + + img->pts = pts; + img->bad_frame = 0; + img->progressive_frame = 1; + + img->draw(img, this->stream); + img->free(img); +} + +static void vpx_flush (video_decoder_t *this_gen) +{ +} + +static void vpx_reset (video_decoder_t *this_gen) +{ + vpx_decoder_t *this = (vpx_decoder_t *) this_gen; + + this->size = 0; +} + +static void vpx_discontinuity (video_decoder_t *this_gen) +{ +} + +/* + * This function frees the video decoder instance allocated to the decoder. + */ +static void vpx_dispose (video_decoder_t *this_gen) +{ + vpx_decoder_t *this = (vpx_decoder_t *) this_gen; + + const void *iter = NULL; + while (1) { + struct vpx_image *img = vpx_codec_get_frame(&this->ctx, &iter); + if (!img) + break; + free(img->user_priv); + img->user_priv = NULL; + } + + vpx_codec_destroy(&this->ctx); + + free (this->buf); + + if (this->decoder_ok) { + this->decoder_ok = 0; + this->stream->video_out->close(this->stream->video_out, this->stream); + } + + free (this_gen); +} + +static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) +{ + vpx_class_t *cls = (vpx_class_t *)class_gen; + vpx_decoder_t *this; + + const struct vpx_codec_iface *iface; + struct vpx_codec_dec_cfg deccfg = { 0 }; + int vp_version; + + switch (cls->buffer_type) { + case BUF_VIDEO_VP8: + iface = &vpx_codec_vp8_dx_algo; + vp_version = 8; + break; +#ifdef HAVE_VPX_VP9_DECODER + case BUF_VIDEO_VP9: + iface = &vpx_codec_vp9_dx_algo; + vp_version = 9; + break; +#endif + default: + return NULL; + } + + + this = (vpx_decoder_t *) calloc(1, sizeof(vpx_decoder_t)); + + this->video_decoder.decode_data = vpx_decode_data; + this->video_decoder.flush = vpx_flush; + this->video_decoder.reset = vpx_reset; + this->video_decoder.discontinuity = vpx_discontinuity; + this->video_decoder.dispose = vpx_dispose; + + this->size = 0; + + this->stream = stream; + this->class = (vpx_class_t *) class_gen; + + this->decoder_ok = 0; + this->buf = NULL; + + xprintf(this->stream->xine, XINE_VERBOSITY_DEBUG, + LOG_MODULE "VP%d: using libvpx version %s\n", + vp_version, vpx_codec_version_str()); + + if (vpx_codec_dec_init(&this->ctx, iface, &deccfg, 0) != VPX_CODEC_OK) { + const char *err = vpx_codec_error(&this->ctx); + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE": Failed to initialize VP%d decoder: %s\n", + vp_version, err); + free(this); + return NULL; + } + + return &this->video_decoder; +} + +static void *init_plugin (xine_t *xine, uint32_t buffer_type, const char *identifier) +{ + vpx_class_t *this; + + this = (vpx_class_t *) calloc(1, sizeof(vpx_class_t)); + + this->decoder_class.open_plugin = open_plugin; + this->decoder_class.identifier = identifier; + this->decoder_class.description = N_("WebM (VP8/VP9) video decoder plugin"); + this->decoder_class.dispose = default_video_decoder_class_dispose; + + this->buffer_type = buffer_type; + + return this; +} + +static void *init_plugin_vp8 (xine_t *xine, void *data) +{ + return init_plugin(xine, BUF_VIDEO_VP8, "libvpx-vp8"); +} + +#ifdef HAVE_VPX_VP9_DECODER +static void *init_plugin_vp9 (xine_t *xine, void *data) +{ + return init_plugin(xine, BUF_VIDEO_VP9, "libvpx-vp9"); +} +#endif + +/* + * exported plugin catalog entry + */ + +static const uint32_t video_types_vp8[] = { + BUF_VIDEO_VP8, + 0 +}; + +#ifdef HAVE_VPX_VP9_DECODER +static const uint32_t video_types_vp9[] = { + BUF_VIDEO_VP9, + 0 +}; +#endif + +static const decoder_info_t dec_info_video_vp8 = { + video_types_vp8, /* supported types */ + 1 /* priority */ +}; + +#ifdef HAVE_VPX_VP9_DECODER +static const decoder_info_t dec_info_video_vp9 = { + video_types_vp9, /* supported types */ + 1 /* priority */ +}; +#endif + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_DECODER, 19, "libvpx-vp8", XINE_VERSION_CODE, &dec_info_video_vp8, init_plugin_vp8 }, +#ifdef HAVE_VPX_VP9_DECODER + { PLUGIN_VIDEO_DECODER, 19, "libvpx-vp9", XINE_VERSION_CODE, &dec_info_video_vp9, init_plugin_vp9 }, +#endif + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; -- cgit v1.2.3