diff --git a/configure.ac b/configure.ac index bb194aa414..52d268d4ea 100644 --- a/configure.ac +++ b/configure.ac @@ -335,6 +335,7 @@ if test "$enable_builtin_codecparsers" = "yes"; then ac_cv_have_gst_mpeg2_parser="no" ac_cv_have_gst_h264_parser="no" ac_cv_have_gst_jpeg_parser="no" + ac_cv_have_gst_vp8_parser="no" else PKG_CHECK_MODULES([GST_CODEC_PARSERS], [gstreamer-codecparsers-$GST_PKG_VERSION >= $GST_PLUGINS_BAD_VERSION_REQUIRED]) @@ -413,6 +414,26 @@ AC_CACHE_CHECK([for JPEG parser], AM_CONDITIONAL([USE_LOCAL_CODEC_PARSERS_JPEG], [test "$ac_cv_have_gst_jpeg_parser" != "yes"]) +dnl ... VP8 parser, not upstream yet +AC_CACHE_CHECK([for VP8 parser], + ac_cv_have_gst_vp8_parser, [ + saved_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GST_CFLAGS $GST_CODEC_PARSERS_CFLAGS" + saved_LIBS="$LIBS" + LIBS="$LIBS $GST_LIBS $GST_CODEC_PARSERS_LIBS" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[GstVp8FrameHdr frame_hdr;]])], + [ac_cv_have_gst_vp8_parser="yes"], + [ac_cv_have_gst_vp8_parser="no"] + ) + CPPFLAGS="$saved_CPPFLAGS" + LIBS="$saved_LIBS" +]) +AM_CONDITIONAL([USE_LOCAL_CODEC_PARSERS_VP8], + [test "$ac_cv_have_gst_vp8_parser" != "yes"]) + case $GST_API_VERSION in 0.10) lt_bias=gst0_vaapi_lt_current_bias;; 1.0) lt_bias=gst1_vaapi_lt_current_bias;; @@ -665,6 +686,31 @@ AC_CACHE_CHECK([for JPEG decoding API], LIBS="$saved_LIBS" ]) +dnl Check for VP8 decoding API (0.34+) +USE_VP8_DECODER=0 +AC_CACHE_CHECK([for VP8 decoding API], + ac_cv_have_vp8_decoding_api, [ + saved_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $LIBVA_CFLAGS" + saved_LIBS="$LIBS" + LIBS="$LIBS $LIBVA_LIBS" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + #include + ]], + [[VAPictureParameterBufferVP8 pic_param; + VASliceParameterBufferVP8 slice_param; + VAProbabilityDataBufferVP8 prob_data; + VAIQMatrixBufferVP8 iq_matrix;]])], + [ac_cv_have_vp8_decoding_api="yes" USE_VP8_DECODER=1], + [ac_cv_have_vp8_decoding_api="no"] + ) + CPPFLAGS="$saved_CPPFLAGS" + LIBS="$saved_LIBS" +]) + + dnl Check for vpp (video post-processing) support USE_VA_VPP=0 AC_CACHE_CHECK([for video post-postprocessing API], @@ -737,6 +783,10 @@ AC_DEFINE_UNQUOTED(USE_JPEG_DECODER, $USE_JPEG_DECODER, [Defined to 1 if JPEG decoder is used]) AM_CONDITIONAL(USE_JPEG_DECODER, test $USE_JPEG_DECODER -eq 1) +AC_DEFINE_UNQUOTED(USE_VP8_DECODER, $USE_VP8_DECODER, + [Defined to 1 if JPEG decoder is used]) +AM_CONDITIONAL(USE_VP8_DECODER, test $USE_VP8_DECODER -eq 1) + AC_DEFINE_UNQUOTED(USE_DRM, $USE_DRM, [Defined to 1 if DRM is enabled]) AM_CONDITIONAL(USE_DRM, test $USE_DRM -eq 1) diff --git a/ext/Makefile.am b/ext/Makefile.am index cfb2bc0300..62f3ec4cb0 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -9,6 +9,7 @@ codecparsers_source_c = \ gstmpeg4parser.c \ gstmpegvideoparser.c \ gstvc1parser.c \ + gstvp8parser.c \ parserutils.c \ $(NULL) @@ -20,6 +21,7 @@ codecparsers_source_h = \ gstmpeg4parser.h \ gstmpegvideoparser.h \ gstvc1parser.h \ + gstvp8parser.h \ parserutils.h \ $(NULL) diff --git a/gst-libs/gst/codecparsers/Makefile.am b/gst-libs/gst/codecparsers/Makefile.am index 1da2217df0..3d414d9514 100644 --- a/gst-libs/gst/codecparsers/Makefile.am +++ b/gst-libs/gst/codecparsers/Makefile.am @@ -40,6 +40,14 @@ gen_source_c += gsth264parser.c gen_source_h += gsth264parser.h endif +if USE_LOCAL_CODEC_PARSERS_VP8 +gen_source_c += gstvp8parser.c +gen_source_h += gstvp8parser.h gstvp8rangedecoder.h vp8utils.h + +gen_source_c += dboolhuff.c gstvp8rangedecoder.c vp8utils.c +gen_source_h += dboolhuff.h +endif + GENFILES = \ $(gen_source_c) \ $(gen_source_h) \ diff --git a/gst-libs/gst/vaapi/Makefile.am b/gst-libs/gst/vaapi/Makefile.am index 4d617137f8..402fff2788 100644 --- a/gst-libs/gst/vaapi/Makefile.am +++ b/gst-libs/gst/vaapi/Makefile.am @@ -56,6 +56,7 @@ libgstvaapi_source_c = \ gstvaapidecoder_objects.c \ gstvaapidecoder_unit.c \ gstvaapidecoder_vc1.c \ + gstvaapidecoder_vp8.c \ gstvaapidisplay.c \ gstvaapidisplaycache.c \ gstvaapifilter.c \ @@ -86,6 +87,7 @@ libgstvaapi_source_h = \ gstvaapidecoder_mpeg2.h \ gstvaapidecoder_mpeg4.h \ gstvaapidecoder_vc1.h \ + gstvaapidecoder_vp8.h \ gstvaapidisplay.h \ gstvaapifilter.h \ gstvaapiimage.h \ diff --git a/gst-libs/gst/vaapi/gstvaapicodec_objects.c b/gst-libs/gst/vaapi/gstvaapicodec_objects.c index d72c176f9b..edf4b35e42 100644 --- a/gst-libs/gst/vaapi/gstvaapicodec_objects.c +++ b/gst-libs/gst/vaapi/gstvaapicodec_objects.c @@ -212,3 +212,39 @@ gst_vaapi_huffman_table_new (GstVaapiDecoder * decoder, return GST_VAAPI_HUFFMAN_TABLE_CAST (object); } #endif +#if USE_VP8_DECODER +GST_VAAPI_CODEC_DEFINE_TYPE (GstVaapiProbabilityTable, + gst_vaapi_probability_table); + +void +gst_vaapi_probability_table_destroy (GstVaapiProbabilityTable * prob_table) +{ + vaapi_destroy_buffer (GET_VA_DISPLAY (prob_table), &prob_table->param_id); + prob_table->param = NULL; +} + +gboolean +gst_vaapi_probability_table_create (GstVaapiProbabilityTable * prob_table, + const GstVaapiCodecObjectConstructorArgs * args) +{ + prob_table->param_id = VA_INVALID_ID; + return vaapi_create_buffer (GET_VA_DISPLAY (prob_table), + GET_VA_CONTEXT (prob_table), + VAProbabilityBufferType, + args->param_size, args->param, &prob_table->param_id, &prob_table->param); +} + +GstVaapiProbabilityTable * +gst_vaapi_probability_table_new (GstVaapiDecoder * decoder, + gconstpointer param, guint param_size) +{ + GstVaapiCodecObject *object; + + object = gst_vaapi_codec_object_new (&GstVaapiProbabilityTableClass, + GST_VAAPI_CODEC_BASE (decoder), param, param_size, NULL, 0, 0); + if (!object) + return NULL; + return GST_VAAPI_PROBABILITY_TABLE_CAST (object); +} + +#endif diff --git a/gst-libs/gst/vaapi/gstvaapicodec_objects.h b/gst-libs/gst/vaapi/gstvaapicodec_objects.h index c7b7902015..163ebf773f 100644 --- a/gst-libs/gst/vaapi/gstvaapicodec_objects.h +++ b/gst-libs/gst/vaapi/gstvaapicodec_objects.h @@ -27,6 +27,9 @@ #include #include +#if USE_VP8_DECODER +#include +#endif G_BEGIN_DECLS @@ -36,6 +39,7 @@ typedef struct _GstVaapiCodecObjectClass GstVaapiCodecObjectClass; typedef struct _GstVaapiIqMatrix GstVaapiIqMatrix; typedef struct _GstVaapiBitPlane GstVaapiBitPlane; typedef struct _GstVaapiHuffmanTable GstVaapiHuffmanTable; +typedef struct _GstVaapiProbabilityTable GstVaapiProbabilityTable; /* ------------------------------------------------------------------------- */ /* --- Base Codec Object --- */ @@ -195,6 +199,33 @@ GstVaapiHuffmanTable * gst_vaapi_huffman_table_new (GstVaapiDecoder * decoder, guint8 * data, guint data_size); +/* ------------------------------------------------------------------------- */ +/* --- Probability (Update) Table --- */ +/* ------------------------------------------------------------------------- */ + +#define GST_VAAPI_PROBABILITY_TABLE_CAST(obj) \ + ((GstVaapiProbabilityTable *)(obj)) + +/** + * GstVaapiProbabilityTable: + * + * A #GstVaapiCodecObject holding an Probability (Update) Table for RAC decoding + */ +struct _GstVaapiProbabilityTable +{ + /*< private > */ + GstVaapiCodecObject parent_instance; + VABufferID param_id; + + /*< public > */ + gpointer param; +}; + +G_GNUC_INTERNAL +GstVaapiProbabilityTable * +gst_vaapi_probability_table_new (GstVaapiDecoder * decoder, + gconstpointer param, guint param_size); + /* ------------------------------------------------------------------------- */ /* --- Helpers to create codec-dependent objects --- */ /* ------------------------------------------------------------------------- */ @@ -230,6 +261,10 @@ static const GstVaapiCodecObjectClass G_PASTE (type, Class) = { \ gst_vaapi_huffman_table_new (GST_VAAPI_DECODER_CAST (decoder), \ NULL, sizeof (G_PASTE (VAHuffmanTableBuffer, codec))) +#define GST_VAAPI_PROBABILITY_TABLE_NEW(codec, decoder) \ + gst_vaapi_probability_table_new (GST_VAAPI_DECODER_CAST (decoder), \ + NULL, sizeof (G_PASTE (VAProbabilityDataBuffer, codec))) + G_END_DECLS #endif /* GST_VAAPI_CODEC_OBJECTS_H */ diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_objects.c b/gst-libs/gst/vaapi/gstvaapidecoder_objects.c index f3fb782990..5f8c0f0e2a 100644 --- a/gst-libs/gst/vaapi/gstvaapidecoder_objects.c +++ b/gst-libs/gst/vaapi/gstvaapidecoder_objects.c @@ -71,6 +71,7 @@ gst_vaapi_picture_destroy (GstVaapiPicture * picture) gst_vaapi_codec_object_replace (&picture->iq_matrix, NULL); gst_vaapi_codec_object_replace (&picture->huf_table, NULL); gst_vaapi_codec_object_replace (&picture->bitplane, NULL); + gst_vaapi_codec_object_replace (&picture->prob_table, NULL); if (picture->proxy) { gst_vaapi_surface_proxy_unref (picture->proxy); @@ -223,6 +224,7 @@ gst_vaapi_picture_decode (GstVaapiPicture * picture) GstVaapiIqMatrix *iq_matrix; GstVaapiBitPlane *bitplane; GstVaapiHuffmanTable *huf_table; + GstVaapiProbabilityTable *prob_table; VADisplay va_display; VAContextID va_context; VAStatus status; @@ -257,6 +259,11 @@ gst_vaapi_picture_decode (GstVaapiPicture * picture) &huf_table->param_id, (void **) &huf_table->param)) return FALSE; + prob_table = picture->prob_table; + if (prob_table && !do_decode (va_display, va_context, + &prob_table->param_id, (void **) &prob_table->param)) + return FALSE; + for (i = 0; i < picture->slices->len; i++) { GstVaapiSlice *const slice = g_ptr_array_index (picture->slices, i); VABufferID va_buffers[2]; diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_objects.h b/gst-libs/gst/vaapi/gstvaapidecoder_objects.h index 91e73214e8..5a36f6318e 100644 --- a/gst-libs/gst/vaapi/gstvaapidecoder_objects.h +++ b/gst-libs/gst/vaapi/gstvaapidecoder_objects.h @@ -134,6 +134,7 @@ struct _GstVaapiPicture GstVaapiIqMatrix *iq_matrix; GstVaapiHuffmanTable *huf_table; GstVaapiBitPlane *bitplane; + GstVaapiProbabilityTable *prob_table; GstClockTime pts; gint32 poc; guint structure; diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_vp8.c b/gst-libs/gst/vaapi/gstvaapidecoder_vp8.c new file mode 100644 index 0000000000..40bf4db473 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_vp8.c @@ -0,0 +1,664 @@ +/* + * gstvaapidecoder_vp8.c - VP8 decoder + * + * Copyright (C) 2013-2014 Intel Corporation + * Author: Halley Zhao + * Author: Gwenole Beauchesne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/** + * SECTION:gstvaapidecoder_vp8 + * @short_description: VP8 decoder + */ + +#include "sysdeps.h" +#include +#include "gstvaapidecoder_vp8.h" +#include "gstvaapidecoder_objects.h" +#include "gstvaapidecoder_priv.h" +#include "gstvaapidisplay_priv.h" +#include "gstvaapiobject_priv.h" + +#include "gstvaapicompat.h" +#include + +#define DEBUG 1 +#include "gstvaapidebug.h" + +#define GST_VAAPI_DECODER_VP8_CAST(decoder) \ + ((GstVaapiDecoderVp8 *)(decoder)) + +typedef struct _GstVaapiDecoderVp8Private GstVaapiDecoderVp8Private; +typedef struct _GstVaapiDecoderVp8Class GstVaapiDecoderVp8Class; + +struct _GstVaapiDecoderVp8Private +{ + GstVaapiProfile profile; + guint width; + guint height; + GstVp8Parser parser; + GstVp8FrameHdr frame_hdr; + GstVaapiPicture *last_picture; + GstVaapiPicture *golden_ref_picture; + GstVaapiPicture *alt_ref_picture; + GstVaapiPicture *current_picture; + GstClockTime pts; + guint size_changed:1; +}; + +/** + * GstVaapiDecoderVp8: + * + * A decoder based on Vp8. + */ +struct _GstVaapiDecoderVp8 +{ + /*< private >*/ + GstVaapiDecoder parent_instance; + + GstVaapiDecoderVp8Private priv; +}; + +/** + * GstVaapiDecoderVp8Class: + * + * A decoder class based on Vp8. + */ +struct _GstVaapiDecoderVp8Class +{ + /*< private >*/ + GstVaapiDecoderClass parent_class; +}; + +static GstVaapiDecoderStatus +get_status (GstVp8ParserResult result) +{ + GstVaapiDecoderStatus status; + + switch (result) { + case GST_VP8_PARSER_OK: + status = GST_VAAPI_DECODER_STATUS_SUCCESS; + break; + case GST_VP8_PARSER_ERROR: + status = GST_VAAPI_DECODER_STATUS_ERROR_BITSTREAM_PARSER; + break; + default: + status = GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + break; + } + return status; +} + +static void +gst_vaapi_decoder_vp8_close (GstVaapiDecoderVp8 * decoder) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + + gst_vaapi_picture_replace (&priv->last_picture, NULL); + gst_vaapi_picture_replace (&priv->golden_ref_picture, NULL); + gst_vaapi_picture_replace (&priv->alt_ref_picture, NULL); + gst_vaapi_picture_replace (&priv->current_picture, NULL); +} + +static gboolean +gst_vaapi_decoder_vp8_open (GstVaapiDecoderVp8 * decoder) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + + gst_vaapi_decoder_vp8_close (decoder); + gst_vp8_parser_init (&priv->parser); + return TRUE; +} + +static void +gst_vaapi_decoder_vp8_destroy (GstVaapiDecoder * base_decoder) +{ + GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder); + + gst_vaapi_decoder_vp8_close (decoder); +} + +static gboolean +gst_vaapi_decoder_vp8_create (GstVaapiDecoder * base_decoder) +{ + GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder); + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + + if (!gst_vaapi_decoder_vp8_open (decoder)) + return FALSE; + + priv->profile = GST_VAAPI_PROFILE_UNKNOWN; + priv->pts = GST_CLOCK_TIME_NONE; + return TRUE; +} + +static GstVaapiDecoderStatus +ensure_context (GstVaapiDecoderVp8 * decoder) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + const GstVaapiProfile profile = GST_VAAPI_PROFILE_VP8; + const GstVaapiEntrypoint entrypoint = GST_VAAPI_ENTRYPOINT_VLD; + gboolean reset_context = FALSE; + + if (priv->profile != profile) { + if (!gst_vaapi_display_has_decoder (GST_VAAPI_DECODER_DISPLAY (decoder), + profile, entrypoint)) + return GST_VAAPI_DECODER_STATUS_ERROR_UNSUPPORTED_PROFILE; + + priv->profile = profile; + reset_context = TRUE; + } + + if (priv->size_changed) { + GST_DEBUG ("size changed"); + priv->size_changed = FALSE; + reset_context = TRUE; + } + + if (reset_context) { + GstVaapiContextInfo info; + + info.profile = priv->profile; + info.entrypoint = entrypoint; + info.width = priv->width; + info.height = priv->height; + info.ref_frames = 3; + reset_context = + gst_vaapi_decoder_ensure_context (GST_VAAPI_DECODER (decoder), &info); + + if (!reset_context) + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +ensure_quant_matrix (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr; + GstVp8Segmentation *const seg = &priv->parser.segmentation; + VAIQMatrixBufferVP8 *iq_matrix; + const gint8 QI_MAX = 127; + gint8 qi, qi_base; + gint i; + + picture->iq_matrix = GST_VAAPI_IQ_MATRIX_NEW (VP8, decoder); + if (!picture->iq_matrix) { + GST_ERROR ("failed to allocate IQ matrix"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + iq_matrix = picture->iq_matrix->param; + + /* Fill in VAIQMatrixBufferVP8 */ + for (i = 0; i < 4; i++) { + if (seg->segmentation_enabled) { + qi_base = seg->quantizer_update_value[i]; + if (!seg->segment_feature_mode) // 0 means delta update + qi_base += frame_hdr->quant_indices.y_ac_qi; + } else + qi_base = frame_hdr->quant_indices.y_ac_qi; + + qi = qi_base; + iq_matrix->quantization_index[i][0] = CLAMP (qi, 0, QI_MAX); + qi = qi_base + frame_hdr->quant_indices.y_dc_delta; + iq_matrix->quantization_index[i][1] = CLAMP (qi, 0, QI_MAX); + qi = qi_base + frame_hdr->quant_indices.y2_dc_delta; + iq_matrix->quantization_index[i][2] = CLAMP (qi, 0, QI_MAX); + qi = qi_base + frame_hdr->quant_indices.y2_ac_delta; + iq_matrix->quantization_index[i][3] = CLAMP (qi, 0, QI_MAX); + qi = qi_base + frame_hdr->quant_indices.uv_dc_delta; + iq_matrix->quantization_index[i][4] = CLAMP (qi, 0, QI_MAX); + qi = qi_base + frame_hdr->quant_indices.uv_ac_delta; + iq_matrix->quantization_index[i][5] = CLAMP (qi, 0, QI_MAX); + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +ensure_probability_table (GstVaapiDecoderVp8 * decoder, + GstVaapiPicture * picture) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr; + VAProbabilityDataBufferVP8 *prob_table; + + picture->prob_table = GST_VAAPI_PROBABILITY_TABLE_NEW (VP8, decoder); + if (!picture->prob_table) { + GST_ERROR ("failed to allocate probality table"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + prob_table = picture->prob_table->param; + + /* Fill in VAProbabilityDataBufferVP8 */ + memcpy (prob_table->dct_coeff_probs, frame_hdr->token_probs.prob, + sizeof (frame_hdr->token_probs.prob)); + + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static void +init_picture (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr; + + picture->structure = GST_VAAPI_PICTURE_STRUCTURE_FRAME; + picture->type = frame_hdr->key_frame ? GST_VAAPI_PICTURE_TYPE_I : + GST_VAAPI_PICTURE_TYPE_P; + picture->pts = priv->pts; + + if (!frame_hdr->show_frame) + GST_VAAPI_PICTURE_FLAG_SET (picture, GST_VAAPI_PICTURE_FLAG_SKIPPED); +} + +static gboolean +fill_picture (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + VAPictureParameterBufferVP8 *const pic_param = picture->param; + GstVp8Parser *const parser = &priv->parser; + GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr; + GstVp8Segmentation *const seg = &parser->segmentation; + gint i; + + /* Fill in VAPictureParameterBufferVP8 */ + pic_param->frame_width = priv->width; + pic_param->frame_height = priv->height; + + pic_param->last_ref_frame = VA_INVALID_SURFACE; + pic_param->golden_ref_frame = VA_INVALID_SURFACE; + pic_param->alt_ref_frame = VA_INVALID_SURFACE; + if (!frame_hdr->key_frame) { + if (priv->last_picture) + pic_param->last_ref_frame = priv->last_picture->surface_id; + if (priv->golden_ref_picture) + pic_param->golden_ref_frame = priv->golden_ref_picture->surface_id; + if (priv->alt_ref_picture) + pic_param->alt_ref_frame = priv->alt_ref_picture->surface_id; + } + pic_param->out_of_loop_frame = VA_INVALID_SURFACE; // not used currently + + pic_param->pic_fields.value = 0; + pic_param->pic_fields.bits.key_frame = !frame_hdr->key_frame; + pic_param->pic_fields.bits.version = frame_hdr->version; + pic_param->pic_fields.bits.segmentation_enabled = seg->segmentation_enabled; + pic_param->pic_fields.bits.update_mb_segmentation_map = + seg->update_mb_segmentation_map; + pic_param->pic_fields.bits.update_segment_feature_data = + seg->update_segment_feature_data; + pic_param->pic_fields.bits.filter_type = frame_hdr->filter_type; + pic_param->pic_fields.bits.sharpness_level = frame_hdr->sharpness_level; + pic_param->pic_fields.bits.loop_filter_adj_enable = + parser->mb_lf_adjust.loop_filter_adj_enable; + pic_param->pic_fields.bits.mode_ref_lf_delta_update = + parser->mb_lf_adjust.mode_ref_lf_delta_update; + pic_param->pic_fields.bits.sign_bias_golden = frame_hdr->sign_bias_golden; + pic_param->pic_fields.bits.sign_bias_alternate = + frame_hdr->sign_bias_alternate; + pic_param->pic_fields.bits.mb_no_coeff_skip = frame_hdr->mb_no_skip_coeff; + + for (i = 0; i < 3; i++) + pic_param->mb_segment_tree_probs[i] = seg->segment_prob[i]; + + for (i = 0; i < 4; i++) { + if (seg->segmentation_enabled) { + pic_param->loop_filter_level[i] = seg->lf_update_value[i]; + if (!seg->segment_feature_mode) + pic_param->loop_filter_level[i] += frame_hdr->loop_filter_level; + } else + pic_param->loop_filter_level[i] = frame_hdr->loop_filter_level; + + pic_param->loop_filter_deltas_ref_frame[i] = + parser->mb_lf_adjust.ref_frame_delta[i]; + pic_param->loop_filter_deltas_mode[i] = + parser->mb_lf_adjust.mb_mode_delta[i]; + } + if ((pic_param->pic_fields.bits.version == 0) + || (pic_param->pic_fields.bits.version == 1)) { + pic_param->pic_fields.bits.loop_filter_disable = + pic_param->loop_filter_level[0] == 0; + } + + pic_param->prob_skip_false = frame_hdr->prob_skip_false; + pic_param->prob_intra = frame_hdr->prob_intra; + pic_param->prob_last = frame_hdr->prob_last; + pic_param->prob_gf = frame_hdr->prob_gf; + + memcpy (pic_param->y_mode_probs, frame_hdr->mode_probs.y_prob, + sizeof (frame_hdr->mode_probs.y_prob)); + memcpy (pic_param->uv_mode_probs, frame_hdr->mode_probs.uv_prob, + sizeof (frame_hdr->mode_probs.uv_prob)); + memcpy (pic_param->mv_probs, frame_hdr->mv_probs.prob, + sizeof (frame_hdr->mv_probs)); + + pic_param->bool_coder_ctx.range = frame_hdr->rd_range; + pic_param->bool_coder_ctx.value = frame_hdr->rd_value; + pic_param->bool_coder_ctx.count = frame_hdr->rd_count; + + return TRUE; +} + +static gboolean +fill_slice (GstVaapiDecoderVp8 * decoder, GstVaapiSlice * slice) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + VASliceParameterBufferVP8 *const slice_param = slice->param; + GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr; + gint i; + + /* Fill in VASliceParameterBufferVP8 */ + slice_param->slice_data_offset = frame_hdr->data_chunk_size; + slice_param->macroblock_offset = frame_hdr->header_size; + slice_param->num_of_partitions = + (1 << frame_hdr->log2_nbr_of_dct_partitions) + 1; + + slice_param->partition_size[0] = + frame_hdr->first_part_size - ((slice_param->macroblock_offset + 7) >> 3); + for (i = 1; i < slice_param->num_of_partitions; i++) + slice_param->partition_size[i] = frame_hdr->partition_size[i - 1]; + for (; i < G_N_ELEMENTS (slice_param->partition_size); i++) + slice_param->partition_size[i] = 0; + + return TRUE; +} + +static GstVaapiDecoderStatus +decode_slice (GstVaapiDecoderVp8 * decoder, GstVaapiPicture * picture, + const guchar * buf, guint buf_size) +{ + GstVaapiSlice *slice; + + slice = GST_VAAPI_SLICE_NEW (VP8, decoder, buf, buf_size); + if (!slice) { + GST_ERROR ("failed to allocate slice"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + + if (!fill_slice (decoder, slice)) { + gst_vaapi_mini_object_unref (GST_VAAPI_MINI_OBJECT (slice)); + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + } + + gst_vaapi_picture_add_slice (GST_VAAPI_PICTURE_CAST (picture), slice); + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +decode_picture (GstVaapiDecoderVp8 * decoder, const guchar * buf, + guint buf_size) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVaapiPicture *picture; + GstVaapiDecoderStatus status; + + status = ensure_context (decoder); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + + /* Create new picture */ + picture = GST_VAAPI_PICTURE_NEW (VP8, decoder); + if (!picture) { + GST_ERROR ("failed to allocate picture"); + return GST_VAAPI_DECODER_STATUS_ERROR_ALLOCATION_FAILED; + } + gst_vaapi_picture_replace (&priv->current_picture, picture); + gst_vaapi_picture_unref (picture); + + status = ensure_quant_matrix (decoder, picture); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + + status = ensure_probability_table (decoder, picture); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + + init_picture (decoder, picture); + if (!fill_picture (decoder, picture)) + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + + return decode_slice (decoder, picture, buf, buf_size); +} + +static void +update_ref_frames (GstVaapiDecoderVp8 * decoder) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVaapiPicture *picture = priv->current_picture; + GstVp8FrameHdr *const frame_hdr = &priv->frame_hdr; + + // update picture reference + if (frame_hdr->key_frame) { + gst_vaapi_picture_replace (&priv->golden_ref_picture, picture); + gst_vaapi_picture_replace (&priv->alt_ref_picture, picture); + } else { + // process refresh_alternate_frame/copy_buffer_to_alternate first + if (frame_hdr->refresh_alternate_frame) { + gst_vaapi_picture_replace (&priv->alt_ref_picture, picture); + } else { + switch (frame_hdr->copy_buffer_to_alternate) { + case 0: + // do nothing + break; + case 1: + gst_vaapi_picture_replace (&priv->alt_ref_picture, + priv->last_picture); + break; + case 2: + gst_vaapi_picture_replace (&priv->alt_ref_picture, + priv->golden_ref_picture); + break; + default: + GST_WARNING + ("WARNING: VP8 decoder: unrecognized copy_buffer_to_alternate"); + } + } + + if (frame_hdr->refresh_golden_frame) { + gst_vaapi_picture_replace (&priv->golden_ref_picture, picture); + } else { + switch (frame_hdr->copy_buffer_to_golden) { + case 0: + // do nothing + break; + case 1: + gst_vaapi_picture_replace (&priv->golden_ref_picture, + priv->last_picture); + break; + case 2: + gst_vaapi_picture_replace (&priv->golden_ref_picture, + priv->alt_ref_picture); + break; + default: + GST_WARNING + ("WARNING: VP8 decoder: unrecognized copy_buffer_to_golden"); + } + } + } + if (frame_hdr->key_frame || frame_hdr->refresh_last) + gst_vaapi_picture_replace (&priv->last_picture, picture); +} + +static GstVaapiDecoderStatus +decode_current_picture (GstVaapiDecoderVp8 * decoder) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVaapiPicture *const picture = priv->current_picture; + + if (!picture) + return GST_VAAPI_DECODER_STATUS_SUCCESS; + + update_ref_frames (decoder); + if (!gst_vaapi_picture_decode (picture)) + goto error; + if (!gst_vaapi_picture_output (picture)) + goto error; + gst_vaapi_picture_replace (&priv->current_picture, NULL); + return GST_VAAPI_DECODER_STATUS_SUCCESS; + +error: + /* XXX: fix for cases where first field failed to be decoded */ + gst_vaapi_picture_replace (&priv->current_picture, NULL); + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; +} + +static GstVaapiDecoderStatus +parse_frame_header (GstVaapiDecoderVp8 * decoder, const guchar * buf, + guint buf_size, GstVp8FrameHdr * frame_hdr) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVp8ParserResult result; + + memset (frame_hdr, 0, sizeof (*frame_hdr)); + result = gst_vp8_parser_parse_frame_header (&priv->parser, frame_hdr, + buf, buf_size); + if (result != GST_VP8_PARSER_OK) + return get_status (result); + + if (frame_hdr->key_frame && + (frame_hdr->width != priv->width || frame_hdr->height != priv->height)) { + priv->width = frame_hdr->width; + priv->height = frame_hdr->height; + priv->size_changed = TRUE; + } + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +gst_vaapi_decoder_vp8_parse (GstVaapiDecoder * base_decoder, + GstAdapter * adapter, gboolean at_eos, GstVaapiDecoderUnit * unit) +{ + GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder); + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + guint flags = 0; + + priv->pts = gst_adapter_prev_pts (adapter, NULL); + unit->size = gst_adapter_available (adapter); + + /* The whole frame is available */ + flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START; + flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE; + flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END; + GST_VAAPI_DECODER_UNIT_FLAG_SET (unit, flags); + return GST_VAAPI_DECODER_STATUS_SUCCESS; + +} + +static GstVaapiDecoderStatus +decode_buffer (GstVaapiDecoderVp8 * decoder, const guchar * buf, guint buf_size) +{ + GstVaapiDecoderVp8Private *const priv = &decoder->priv; + GstVaapiDecoderStatus status; + + status = parse_frame_header (decoder, buf, buf_size, &priv->frame_hdr); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + + return decode_picture (decoder, buf, buf_size); +} + +GstVaapiDecoderStatus +gst_vaapi_decoder_vp8_decode (GstVaapiDecoder * base_decoder, + GstVaapiDecoderUnit * unit) +{ + GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder); + GstVaapiDecoderStatus status; + GstBuffer *const buffer = + GST_VAAPI_DECODER_CODEC_FRAME (decoder)->input_buffer; + GstMapInfo map_info; + + if (!gst_buffer_map (buffer, &map_info, GST_MAP_READ)) { + GST_ERROR ("failed to map buffer"); + return GST_VAAPI_DECODER_STATUS_ERROR_UNKNOWN; + } + + status = decode_buffer (decoder, map_info.data + unit->offset, unit->size); + gst_buffer_unmap (buffer, &map_info); + if (status != GST_VAAPI_DECODER_STATUS_SUCCESS) + return status; + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +gst_vaapi_decoder_vp8_start_frame (GstVaapiDecoder * base_decoder, + GstVaapiDecoderUnit * base_unit) +{ + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static GstVaapiDecoderStatus +gst_vaapi_decoder_vp8_end_frame (GstVaapiDecoder * base_decoder) +{ + GstVaapiDecoderVp8 *const decoder = GST_VAAPI_DECODER_VP8_CAST (base_decoder); + + return decode_current_picture (decoder); +} + +static GstVaapiDecoderStatus +gst_vaapi_decoder_vp8_flush (GstVaapiDecoder * base_decoder) +{ + return GST_VAAPI_DECODER_STATUS_SUCCESS; +} + +static void +gst_vaapi_decoder_vp8_class_init (GstVaapiDecoderVp8Class * klass) +{ + GstVaapiMiniObjectClass *const object_class = + GST_VAAPI_MINI_OBJECT_CLASS (klass); + GstVaapiDecoderClass *const decoder_class = GST_VAAPI_DECODER_CLASS (klass); + + object_class->size = sizeof (GstVaapiDecoderVp8); + object_class->finalize = (GDestroyNotify) gst_vaapi_decoder_finalize; + + decoder_class->create = gst_vaapi_decoder_vp8_create; + decoder_class->destroy = gst_vaapi_decoder_vp8_destroy; + decoder_class->parse = gst_vaapi_decoder_vp8_parse; + decoder_class->decode = gst_vaapi_decoder_vp8_decode; + decoder_class->start_frame = gst_vaapi_decoder_vp8_start_frame; + decoder_class->end_frame = gst_vaapi_decoder_vp8_end_frame; + decoder_class->flush = gst_vaapi_decoder_vp8_flush; +} + +static inline const GstVaapiDecoderClass * +gst_vaapi_decoder_vp8_class (void) +{ + static GstVaapiDecoderVp8Class g_class; + static gsize g_class_init = FALSE; + + if (g_once_init_enter (&g_class_init)) { + gst_vaapi_decoder_vp8_class_init (&g_class); + g_once_init_leave (&g_class_init, TRUE); + } + return GST_VAAPI_DECODER_CLASS (&g_class); +} + +/** + * gst_vaapi_decoder_vp8_new: + * @display: a #GstVaapiDisplay + * @caps: a #GstCaps holding codec information + * + * Creates a new #GstVaapiDecoder for VP8 decoding. The @caps can + * hold extra information like codec-data and pictured coded size. + * + * Return value: the newly allocated #GstVaapiDecoder object + */ +GstVaapiDecoder * +gst_vaapi_decoder_vp8_new (GstVaapiDisplay * display, GstCaps * caps) +{ + return gst_vaapi_decoder_new (gst_vaapi_decoder_vp8_class (), display, caps); +} diff --git a/gst-libs/gst/vaapi/gstvaapidecoder_vp8.h b/gst-libs/gst/vaapi/gstvaapidecoder_vp8.h new file mode 100644 index 0000000000..43a88fdc85 --- /dev/null +++ b/gst-libs/gst/vaapi/gstvaapidecoder_vp8.h @@ -0,0 +1,38 @@ +/* + * gstvaapidecoder_vp8.h - VP8 decoder + * + * Copyright (C) 2013-2014 Intel Corporation + * Author: Halley Zhao + * Author: Gwenole Beauchesne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#ifndef GST_VAAPI_DECODER_VP8_H +#define GST_VAAPI_DECODER_VP8_H + +#include + +G_BEGIN_DECLS + +typedef struct _GstVaapiDecoderVp8 GstVaapiDecoderVp8; + +GstVaapiDecoder * +gst_vaapi_decoder_vp8_new (GstVaapiDisplay * display, GstCaps * caps); + +G_END_DECLS + +#endif /* GST_VAAPI_DECODER_VP8_H */ diff --git a/gst-libs/gst/vaapi/gstvaapiprofile.c b/gst-libs/gst/vaapi/gstvaapiprofile.c index ad3441a10d..76e5d0e0a3 100644 --- a/gst-libs/gst/vaapi/gstvaapiprofile.c +++ b/gst-libs/gst/vaapi/gstvaapiprofile.c @@ -65,6 +65,7 @@ static const GstVaapiCodecMap gst_vaapi_codecs[] = { { GST_VAAPI_CODEC_WMV3, "wmv3" }, { GST_VAAPI_CODEC_VC1, "vc1" }, { GST_VAAPI_CODEC_JPEG, "jpeg" }, + { GST_VAAPI_CODEC_VP8, "vp8" }, { 0, } }; @@ -125,6 +126,8 @@ static const GstVaapiProfileMap gst_vaapi_profiles[] = { "image/jpeg", "baseline" }, #endif + {GST_VAAPI_PROFILE_VP8, VAProfileVP8Version0_3, + "video/x-vp8", "Version0_3"}, { 0, } }; diff --git a/gst-libs/gst/vaapi/gstvaapiprofile.h b/gst-libs/gst/vaapi/gstvaapiprofile.h index be74a0afe5..24dde8988a 100644 --- a/gst-libs/gst/vaapi/gstvaapiprofile.h +++ b/gst-libs/gst/vaapi/gstvaapiprofile.h @@ -51,6 +51,7 @@ typedef enum { GST_VAAPI_CODEC_WMV3 = GST_MAKE_FOURCC('W','M','V',0), GST_VAAPI_CODEC_VC1 = GST_MAKE_FOURCC('V','C','1',0), GST_VAAPI_CODEC_JPEG = GST_MAKE_FOURCC('J','P','G',0), + GST_VAAPI_CODEC_VP8 = GST_MAKE_FOURCC('V','P','8',0), } GstVaapiCodec; /** @@ -151,6 +152,7 @@ typedef enum { GST_VAAPI_PROFILE_VC1_MAIN = GST_VAAPI_MAKE_PROFILE(VC1,2), GST_VAAPI_PROFILE_VC1_ADVANCED = GST_VAAPI_MAKE_PROFILE(VC1,3), GST_VAAPI_PROFILE_JPEG_BASELINE = GST_VAAPI_MAKE_PROFILE(JPEG,1), + GST_VAAPI_PROFILE_VP8 = GST_VAAPI_MAKE_PROFILE(VP8,1), } GstVaapiProfile; /** diff --git a/gst/vaapi/gstvaapidecode.c b/gst/vaapi/gstvaapidecode.c index 98d7916838..9b57480884 100644 --- a/gst/vaapi/gstvaapidecode.c +++ b/gst/vaapi/gstvaapidecode.c @@ -49,6 +49,7 @@ #include #include #include +#include #define GST_PLUGIN_NAME "vaapidecode" #define GST_PLUGIN_DESC "A VA-API based video decoder" @@ -67,6 +68,7 @@ static const char gst_vaapidecode_sink_caps_str[] = GST_CAPS_CODEC("video/x-h263") GST_CAPS_CODEC("video/x-h264") GST_CAPS_CODEC("video/x-wmv") + GST_CAPS_CODEC("video/x-vp8") GST_CAPS_CODEC("image/jpeg") ; @@ -631,6 +633,9 @@ gst_vaapidecode_create(GstVaapiDecode *decode, GstCaps *caps) decode->decoder = gst_vaapi_decoder_jpeg_new(dpy, caps); break; #endif + case GST_VAAPI_CODEC_VP8: + decode->decoder = gst_vaapi_decoder_vp8_new(dpy, caps); + break; default: decode->decoder = NULL; break;