summaryrefslogtreecommitdiff
path: root/src/video_dec/libvdpau/vdpau_h264.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_dec/libvdpau/vdpau_h264.c')
-rw-r--r--src/video_dec/libvdpau/vdpau_h264.c1014
1 files changed, 1014 insertions, 0 deletions
diff --git a/src/video_dec/libvdpau/vdpau_h264.c b/src/video_dec/libvdpau/vdpau_h264.c
new file mode 100644
index 000000000..25ed62295
--- /dev/null
+++ b/src/video_dec/libvdpau/vdpau_h264.c
@@ -0,0 +1,1014 @@
+/*
+ * Copyright (C) 2008 Julian Scheel
+ *
+ * kate: space-indent on; indent-width 2; mixedindent off; indent-mode cstyle; remove-trailing-space on;
+ *
+ * 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
+ *
+ * vdpau_h264.c: H264 Video Decoder utilizing nvidia VDPAU engine
+ */
+
+#define LOG_MODULE "vdpau_h264"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <vdpau/vdpau.h>
+
+#include <xine/xine_internal.h>
+#include <xine/video_out.h>
+#include <xine/buffer.h>
+#include <xine/xineutils.h>
+#include "bswap.h"
+#include "accel_vdpau.h"
+#include "h264_parser.h"
+#include "dpb.h"
+#include "cpb.h"
+
+//#define DEBUG_H264
+
+#define VIDEOBUFSIZE 128*1024
+
+typedef struct {
+ video_decoder_class_t decoder_class;
+} vdpau_h264_class_t;
+
+typedef struct vdpau_h264_decoder_s {
+ video_decoder_t video_decoder; /* parent video decoder structure */
+
+ vdpau_h264_class_t *class;
+ xine_stream_t *stream;
+
+ /* these are traditional variables in a video decoder object */
+ uint64_t video_step; /* frame duration in pts units */
+ uint64_t reported_video_step; /* frame duration in pts units */
+
+ int width; /* the width of a video frame */
+ int height; /* the height of a video frame */
+ double ratio; /* the width to height ratio */
+
+
+ struct h264_parser *nal_parser; /* h264 nal parser. extracts stream data for vdpau */
+
+ struct decoded_picture *incomplete_pic;
+ uint32_t last_top_field_order_cnt;
+
+ int have_frame_boundary_marks;
+ int wait_for_frame_start;
+
+ VdpDecoder decoder;
+ int decoder_started;
+ int progressive_cnt; /* count of progressive marked frames in line */
+
+ VdpColorStandard color_standard;
+ VdpDecoderProfile profile;
+ vdpau_accel_t *vdpau_accel;
+
+ xine_t *xine;
+
+ struct coded_picture *completed_pic;
+ vo_frame_t *dangling_img;
+
+ uint8_t *codec_private;
+ uint32_t codec_private_len;
+
+ int vdp_runtime_nr;
+
+ int reset;
+
+} vdpau_h264_decoder_t;
+
+static void vdpau_h264_reset (video_decoder_t *this_gen);
+static void vdpau_h264_flush (video_decoder_t *this_gen);
+
+/**************************************************************************
+ * vdpau_h264 specific decode functions
+ *************************************************************************/
+
+/**************************************************************************
+ * xine video plugin functions
+ *************************************************************************/
+
+#ifdef DEBUG_H264
+static inline void dump_pictureinfo_h264(VdpPictureInfoH264 *pic)
+{
+ printf("C: slice_count: %d\n", pic->slice_count);
+ printf("C: field_order_cnt[0]: %d\n", pic->field_order_cnt[0]);
+ printf("C: field_order_cnt[1]: %d\n", pic->field_order_cnt[1]);
+ printf("C: is_reference: %d\n", pic->is_reference);
+ printf("C: frame_num: %d\n", pic->frame_num);
+ printf("C: field_pic_flag: %d\n", pic->field_pic_flag);
+ printf("C: bottom_field_flag: %d\n", pic->bottom_field_flag);
+ printf("C: num_ref_frames: %d\n", pic->num_ref_frames);
+ printf("C: mb_adaptive_frame_field_flag: %d\n", pic->mb_adaptive_frame_field_flag);
+ printf("C: constrained_intra_pred_flag: %d\n", pic->constrained_intra_pred_flag);
+ printf("C: weighted_pred_flag: %d\n", pic->weighted_pred_flag);
+ printf("C: weighted_bipred_idc: %d\n", pic->weighted_bipred_idc);
+ printf("C: frame_mbs_only_flag: %d\n", pic->frame_mbs_only_flag);
+ printf("C: transform_8x8_mode_flag: %d\n", pic->transform_8x8_mode_flag);
+ printf("C: chroma_qp_index_offset: %d\n", pic->chroma_qp_index_offset);
+ printf("C: second_chroma_qp_index_offset: %d\n", pic->second_chroma_qp_index_offset);
+ printf("C: pic_init_qp_minus26: %d\n", pic->pic_init_qp_minus26);
+ printf("C: num_ref_idx_l0_active_minus1: %d\n", pic->num_ref_idx_l0_active_minus1);
+ printf("C: num_ref_idx_l1_active_minus1: %d\n", pic->num_ref_idx_l1_active_minus1);
+ printf("C: log2_max_frame_num_minus4: %d\n", pic->log2_max_frame_num_minus4);
+ printf("C: pic_order_cnt_type: %d\n", pic->pic_order_cnt_type);
+ printf("C: log2_max_pic_order_cnt_lsb_minus4: %d\n", pic->log2_max_pic_order_cnt_lsb_minus4);
+ printf("C: delta_pic_order_always_zero_flag: %d\n", pic->delta_pic_order_always_zero_flag);
+ printf("C: direct_8x8_inference_flag: %d\n", pic->direct_8x8_inference_flag);
+ printf("C: entropy_coding_mode_flag: %d\n", pic->entropy_coding_mode_flag);
+ printf("C: pic_order_present_flag: %d\n", pic->pic_order_present_flag);
+ printf("C: deblocking_filter_control_present_flag: %d\n", pic->deblocking_filter_control_present_flag);
+ printf("C: redundant_pic_cnt_present_flag: %d\n", pic->redundant_pic_cnt_present_flag);
+
+ int i, j;
+ for(i = 0; i < 6; i++) {
+ printf("C: scalint_list4x4[%d]:\nC:", i);
+ for(j = 0; j < 16; j++) {
+ printf(" [%d]", pic->scaling_lists_4x4[i][j]);
+ if(j%8 == 0)
+ printf("\nC:");
+ }
+ printf("C: \n");
+ }
+ for(i = 0; i < 2; i++) {
+ printf("C: scalint_list8x8[%d]:\nC:", i);
+ for(j = 0; j < 64; j++) {
+ printf(" [%d] ", pic->scaling_lists_8x8[i][j]);
+ if(j%8 == 0)
+ printf("\nC:");
+ }
+ printf("C: \n");
+ }
+
+ //int i;
+ for(i = 0; i < 16; i++) {
+ if(pic->referenceFrames[i].surface != VDP_INVALID_HANDLE) {
+ printf("C: -------------------\n");
+ printf("C: Reference Frame %d:\n", i);
+ printf("C: frame_idx: %d\n", pic->referenceFrames[i].frame_idx);
+ printf("C: field_order_cnt[0]: %d\n", pic->referenceFrames[i].field_order_cnt[0]);
+ printf("C: field_order_cnt[1]: %d\n", pic->referenceFrames[i].field_order_cnt[0]);
+ printf("C: is_long_term: %d\n", pic->referenceFrames[i].is_long_term);
+ printf("C: top_is_reference: %d\n", pic->referenceFrames[i].top_is_reference);
+ printf("C: bottom_is_reference: %d\n", pic->referenceFrames[i].bottom_is_reference);
+ }
+ }
+ printf("C: ---------------------------------------------------------------\n");
+ /*memcpy(pic.scaling_lists_4x4, pps->scaling_lists_4x4, 6*16);
+ memcpy(pic.scaling_lists_8x8, pps->scaling_lists_8x8, 2*64);
+ memcpy(pic.referenceFrames, this->reference_frames, sizeof(this->reference_frames));*/
+
+}
+#endif
+
+static void set_ratio(video_decoder_t *this_gen)
+{
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *)this_gen;
+
+ this->ratio = (double)this->width / (double)this->height;
+ if(this->completed_pic->sps_nal->sps.vui_parameters.aspect_ration_info_present_flag) {
+ switch(this->completed_pic->sps_nal->sps.vui_parameters.aspect_ratio_idc) {
+ case ASPECT_1_1:
+ this->ratio = 1 * this->ratio;
+ break;
+ case ASPECT_12_11:
+ this->ratio *= 12.0/11.0;
+ break;
+ case ASPECT_10_11:
+ this->ratio *= 10.0/11.0;
+ break;
+ case ASPECT_16_11:
+ this->ratio *= 16.0/11.0;
+ break;
+ case ASPECT_40_33:
+ this->ratio *= 40.0/33.0;
+ break;
+ case ASPECT_24_11:
+ this->ratio *= 24.0/11.0;
+ break;
+ case ASPECT_20_11:
+ this->ratio *= 20.0/11.0;
+ break;
+ case ASPECT_32_11:
+ this->ratio *= 32.0/11.0;
+ break;
+ case ASPECT_80_33:
+ this->ratio *= 80.0/33.0;
+ break;
+ case ASPECT_18_11:
+ this->ratio *= 18.0/11.0;
+ break;
+ case ASPECT_15_11:
+ this->ratio *= 15.0/11.0;
+ break;
+ case ASPECT_64_33:
+ this->ratio *= 64.0/33.0;
+ break;
+ case ASPECT_160_99:
+ this->ratio *= 160.0/99.0;
+ break;
+ case ASPECT_4_3:
+ this->ratio *= 4.0/3.0;
+ break;
+ case ASPECT_3_2:
+ this->ratio *= 3.0/2.0;
+ break;
+ case ASPECT_2_1:
+ this->ratio *= 2.0/1.0;
+ break;
+ case ASPECT_EXTENDED_SAR:
+ this->ratio *=
+ (double)this->completed_pic->sps_nal->sps.vui_parameters.sar_width/
+ (double)this->completed_pic->sps_nal->sps.vui_parameters.sar_height;
+ break;
+ }
+ }
+}
+
+static void fill_vdpau_pictureinfo_h264(video_decoder_t *this_gen, uint32_t slice_count, VdpPictureInfoH264 *pic)
+{
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *)this_gen;
+
+ struct pic_parameter_set_rbsp *pps = &this->completed_pic->pps_nal->pps;
+ struct seq_parameter_set_rbsp *sps = &this->completed_pic->sps_nal->sps;
+ struct slice_header *slc = &this->completed_pic->slc_nal->slc;
+
+ pic->slice_count = slice_count;
+ pic->field_order_cnt[0] = this->completed_pic->top_field_order_cnt;
+ pic->field_order_cnt[1] = this->completed_pic->bottom_field_order_cnt;
+ pic->is_reference =
+ (this->completed_pic->flag_mask & REFERENCE) ? VDP_TRUE : VDP_FALSE;
+ pic->frame_num = slc->frame_num;
+ pic->field_pic_flag = slc->field_pic_flag;
+ pic->bottom_field_flag = slc->bottom_field_flag;
+ pic->num_ref_frames = sps->num_ref_frames;
+ pic->mb_adaptive_frame_field_flag = sps->mb_adaptive_frame_field_flag && !slc->field_pic_flag;
+ pic->constrained_intra_pred_flag = pps->constrained_intra_pred_flag;
+ pic->weighted_pred_flag = pps->weighted_pred_flag;
+ pic->weighted_bipred_idc = pps->weighted_bipred_idc;
+ pic->frame_mbs_only_flag = sps->frame_mbs_only_flag;
+ pic->transform_8x8_mode_flag = pps->transform_8x8_mode_flag;
+ pic->chroma_qp_index_offset = pps->chroma_qp_index_offset;
+ pic->second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset;
+ pic->pic_init_qp_minus26 = pps->pic_init_qp_minus26;
+ pic->num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_active_minus1;
+ pic->num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_active_minus1;
+ pic->log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
+ pic->pic_order_cnt_type = sps->pic_order_cnt_type;
+ pic->log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_pic_order_cnt_lsb_minus4;
+ pic->delta_pic_order_always_zero_flag = sps->delta_pic_order_always_zero_flag;
+ pic->direct_8x8_inference_flag = sps->direct_8x8_inference_flag;
+ pic->entropy_coding_mode_flag = pps->entropy_coding_mode_flag;
+ pic->pic_order_present_flag = pps->pic_order_present_flag;
+ pic->deblocking_filter_control_present_flag = pps->deblocking_filter_control_present_flag;
+ pic->redundant_pic_cnt_present_flag = pps->redundant_pic_cnt_present_flag;
+
+ memcpy(pic->scaling_lists_4x4, pps->scaling_lists_4x4, sizeof(pic->scaling_lists_4x4));
+ memcpy(pic->scaling_lists_8x8, pps->scaling_lists_8x8, sizeof(pic->scaling_lists_8x8));
+
+ /* set num_ref_frames to the number of actually available reference frames,
+ * if this is not set generation 3 decoders will fail. */
+ /*pic->num_ref_frames =*/
+ fill_vdpau_reference_list(this->nal_parser->dpb, pic->referenceFrames);
+
+}
+
+static int check_progressive(video_decoder_t *this_gen, struct decoded_picture *dpic)
+{
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *)this_gen;
+ int progressive = 0;
+ int i;
+
+ for(i = 0; i < 2; i++) {
+ struct coded_picture *pic = dpic->coded_pic[i];
+ if (!pic) {
+ continue;
+ }
+
+ if (pic->flag_mask & PIC_STRUCT_PRESENT && pic->sei_nal != NULL) {
+ uint8_t pic_struct = pic->sei_nal->sei.pic_timing.pic_struct;
+
+ if (pic_struct == DISP_FRAME) {
+ progressive = 1;
+ continue;
+ } else if (pic_struct == DISP_TOP_BOTTOM ||
+ pic_struct == DISP_BOTTOM_TOP) {
+ progressive = 0;
+ break;
+ }
+
+ /* FIXME: seems unreliable, maybe it's has to be interpreted more complex */
+ /*if (pic->sei_nal->sei.pic_timing.ct_type == CT_INTERLACED) {
+ return 0;
+ } else if (pic->sei_nal->sei.pic_timing.ct_type == CT_PROGRESSIVE) {
+ return 1;
+ } */
+ }
+
+ if (pic->slc_nal->slc.field_pic_flag && pic->pps_nal->pps.pic_order_present_flag) {
+ if(pic->slc_nal->slc.delta_pic_order_cnt_bottom == 1 ||
+ pic->slc_nal->slc.delta_pic_order_cnt_bottom == -1) {
+ progressive = 0;
+ break;
+ } else {
+ progressive = 1;
+ continue;
+ }
+ }
+ if (!pic->slc_nal->slc.field_pic_flag && pic->sps_nal->sps.frame_mbs_only_flag) {
+ progressive = 1;
+ continue;
+ }
+ }
+
+ if (progressive) {
+ this->progressive_cnt++;
+ } else {
+ this->progressive_cnt = 0;
+ }
+
+ /* only switch to progressive mode if at least 5
+ * frames in order were marked as progressive */
+ return (this->progressive_cnt >= 5);
+}
+
+static int vdpau_decoder_init(video_decoder_t *this_gen)
+{
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *)this_gen;
+ vo_frame_t *img;
+
+ if(this->width == 0) {
+ this->width = this->completed_pic->sps_nal->sps.pic_width;
+ this->height = this->completed_pic->sps_nal->sps.pic_height;
+ }
+
+ set_ratio(this_gen);
+
+ _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, ((double)10000*this->ratio) );
+ _x_stream_info_set( this->stream, XINE_STREAM_INFO_FRAME_DURATION, (this->reported_video_step = this->video_step) );
+ _x_meta_info_set_utf8( this->stream, XINE_META_INFO_VIDEOCODEC, "H264/AVC (vdpau)" );
+ xine_event_t event;
+ xine_format_change_data_t data;
+ event.type = XINE_EVENT_FRAME_FORMAT_CHANGE;
+ event.stream = this->stream;
+ event.data = &data;
+ event.data_length = sizeof(data);
+ data.width = this->width;
+ data.height = this->height;
+ data.aspect = this->ratio;
+ xine_event_send( this->stream, &event );
+
+ switch(this->completed_pic->sps_nal->sps.profile_idc) {
+ case 100:
+ this->profile = VDP_DECODER_PROFILE_H264_HIGH;
+ break;
+ case 77:
+ this->profile = VDP_DECODER_PROFILE_H264_MAIN;
+ break;
+ case 66:
+ default:
+ // nvidia's VDPAU doesn't support BASELINE. But most (every?) streams marked BASELINE do not use BASELINE specifics,
+ // so, just force MAIN.
+ //this->profile = VDP_DECODER_PROFILE_H264_BASELINE;
+ this->profile = VDP_DECODER_PROFILE_H264_MAIN;
+ break;
+ }
+
+ // Level 4.1 limits:
+ int ref_frames = 0;
+ if(this->completed_pic->sps_nal->sps.num_ref_frames) {
+ ref_frames = this->completed_pic->sps_nal->sps.num_ref_frames;
+ } else {
+ uint32_t round_width = (this->width + 15) & ~15;
+ uint32_t round_height = (this->height + 15) & ~15;
+ uint32_t surf_size = (round_width * round_height * 3) / 2;
+ ref_frames = (12 * 1024 * 1024) / surf_size;
+ }
+
+ if (ref_frames > 16) {
+ ref_frames = 16;
+ }
+
+ xprintf(this->xine, XINE_VERBOSITY_LOG, "Allocate %d reference frames\n",
+ ref_frames);
+ /* get the vdpau context from vo */
+ //(this->stream->video_out->open) (this->stream->video_out, this->stream);
+ img = this->stream->video_out->get_frame (this->stream->video_out,
+ this->width, this->height,
+ this->ratio,
+ XINE_IMGFMT_VDPAU, VO_BOTH_FIELDS | this->reset);
+ this->reset = 0;
+
+ this->vdpau_accel = (vdpau_accel_t*)img->accel_data;
+
+ img->free(img);
+ img = NULL;
+
+ /*VdpBool is_supported;
+ uint32_t max_level, max_references, max_width, max_height;*/
+ if(this->vdpau_accel->vdp_runtime_nr > 0) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ "Create decoder: vdp_device: %d, profile: %d, res: %dx%d\n",
+ this->vdpau_accel->vdp_device, this->profile, this->width, this->height);
+
+ VdpStatus status = this->vdpau_accel->vdp_decoder_create(this->vdpau_accel->vdp_device,
+ this->profile, this->width, this->height, 16, &this->decoder);
+
+ if(status != VDP_STATUS_OK) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG, "vdpau_h264: ERROR: VdpDecoderCreate returned status != OK (%s)\n", this->vdpau_accel->vdp_get_error_string(status));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void draw_frames(video_decoder_t *this_gen, int flush)
+{
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *)this_gen;
+
+ struct decoded_picture *decoded_pic = NULL;
+ while ((decoded_pic = dpb_get_next_out_picture(this->nal_parser->dpb, flush)) != NULL) {
+ decoded_pic->img->top_field_first = dp_top_field_first(decoded_pic);
+ decoded_pic->img->progressive_frame = check_progressive(this_gen, decoded_pic);
+#ifdef DEBUG_H264
+ printf("progressive: %d\n", decoded_pic->img->progressive_frame);
+#endif
+ if (flush) {
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "h264 flush, draw pts: %"PRId64"\n", decoded_pic->img->pts);
+ }
+
+ decoded_pic->img->draw(decoded_pic->img, this->stream);
+ dpb_unmark_picture_delayed(this->nal_parser->dpb, decoded_pic);
+ decoded_pic = NULL;
+ }
+}
+
+static int vdpau_decoder_render(video_decoder_t *this_gen, VdpBitstreamBuffer *vdp_buffer, uint32_t slice_count)
+{
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *)this_gen;
+ vo_frame_t *img = NULL;
+
+ /* if we wait for a second field for this frame, we
+ * have to render to the same surface again.
+ */
+ if (this->incomplete_pic) {
+ img = this->incomplete_pic->img;
+ }
+
+ // FIXME: what is if this is the second field of a field coded
+ // picture? - should we keep the first field in dpb?
+ if(this->completed_pic->flag_mask & IDR_PIC) {
+ dpb_flush(this->nal_parser->dpb);
+ if(this->incomplete_pic) {
+ release_decoded_picture(this->incomplete_pic);
+ this->incomplete_pic = NULL;
+ }
+ }
+
+ struct seq_parameter_set_rbsp *sps = &this->completed_pic->sps_nal->sps;
+ struct slice_header *slc = &this->completed_pic->slc_nal->slc;
+
+ if(sps->vui_parameters_present_flag &&
+ sps->vui_parameters.timing_info_present_flag &&
+ this->video_step == 0) {
+ this->video_step = 2*90000/(1/((double)sps->vui_parameters.num_units_in_tick/(double)sps->vui_parameters.time_scale));
+ }
+
+ /* go and decode a frame */
+
+ /* check if we expect a second field, but got a frame */
+ if (this->incomplete_pic && img) {
+ if ((this->completed_pic->slc_nal->slc.frame_num !=
+ this->incomplete_pic->coded_pic[0]->slc_nal->slc.frame_num) ||
+ !slc->field_pic_flag) {
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG, "H264 warning: Expected a second field, stream might be broken\n");
+
+ /* remove this pic from dpb, as it is not complete */
+ dpb_unmark_picture_delayed(this->nal_parser->dpb, this->incomplete_pic);
+ dpb_unmark_reference_picture(this->nal_parser->dpb, this->incomplete_pic);
+
+ release_decoded_picture(this->incomplete_pic);
+ this->incomplete_pic = NULL;
+ img = NULL;
+ }
+ }
+
+
+ VdpPictureInfoH264 pic;
+
+ fill_vdpau_pictureinfo_h264(this_gen, slice_count, &pic);
+
+#ifdef DEBUG_H264
+ dump_pictureinfo_h264(&pic);
+
+ int i;
+ printf("E: Bytes used: %d\n", vdp_buffer->bitstream_bytes);
+ printf("E: Decode data: \nE:");
+ for(i = 0; i < ((vdp_buffer->bitstream_bytes < 20) ? vdp_buffer->bitstream_bytes : 20); i++) {
+ printf("%02x ", ((uint8_t*)vdp_buffer->bitstream)[i]);
+ if((i+1) % 10 == 0)
+ printf("\nE:");
+ }
+ printf("\n...\n");
+ for(i = vdp_buffer->bitstream_bytes - 20; i < vdp_buffer->bitstream_bytes; i++) {
+ printf("%02x ", ((uint8_t*)vdp_buffer->bitstream)[i]);
+ if((i+1) % 10 == 0)
+ printf("\nE:");
+ }
+ printf("\nE: ---------------------------------------------------------------\n");
+#endif
+
+ if(!this->decoder_started && !pic.is_reference)
+ return 0;
+
+ this->decoder_started = 1;
+
+ if(img == NULL) {
+ img = this->stream->video_out->get_frame (this->stream->video_out,
+ this->width, this->height,
+ this->ratio,
+ XINE_IMGFMT_VDPAU, VO_BOTH_FIELDS);
+ this->vdpau_accel = (vdpau_accel_t*)img->accel_data;
+
+ img->duration = this->video_step;
+ img->pts = this->completed_pic->pts;
+
+ if (this->dangling_img) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ "broken stream: current img wasn't processed -- freeing it!\n");
+ this->dangling_img->free(this->dangling_img);
+ }
+ this->dangling_img = img;
+ } else {
+ if (img->pts == 0) {
+ img->pts = this->completed_pic->pts;
+ }
+ }
+
+ if(this->vdp_runtime_nr != *(this->vdpau_accel->current_vdp_runtime_nr)) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ "VDPAU was preempted. Reinitialise the decoder.\n");
+ this->decoder = VDP_INVALID_HANDLE;
+ vdpau_h264_reset(this_gen);
+ this->vdp_runtime_nr = this->vdpau_accel->vdp_runtime_nr;
+ return 0;
+ }
+
+ VdpVideoSurface surface = this->vdpau_accel->surface;
+
+ /*xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "Decode: NUM: %d, REF: %d, BYTES: %d, PTS: %lld\n", pic.frame_num, pic.is_reference, vdp_buffer->bitstream_bytes, this->completed_pic->pts);*/
+ VdpStatus status = this->vdpau_accel->vdp_decoder_render(this->decoder,
+ surface, (VdpPictureInfo*)&pic, 1, vdp_buffer);
+
+ /* free the image data */
+ if(((uint8_t*)vdp_buffer->bitstream) != NULL) {
+ free((uint8_t*)vdp_buffer->bitstream);
+ }
+
+ process_mmc_operations(this->nal_parser, this->completed_pic);
+
+ if(status != VDP_STATUS_OK)
+ {
+ xprintf(this->xine, XINE_VERBOSITY_LOG, "vdpau_h264: Decoder failure: %s\n", this->vdpau_accel->vdp_get_error_string(status));
+ if (this->dangling_img)
+ this->dangling_img->free(this->dangling_img);
+ img = NULL;
+ this->dangling_img = NULL;
+ free_coded_picture(this->completed_pic);
+ this->completed_pic = NULL;
+ }
+ else {
+ img->bad_frame = 0;
+
+ if(!img->progressive_frame && this->completed_pic->repeat_pic)
+ img->repeat_first_field = 1;
+ //else if(img->progressive_frame && this->nal_parser->current_nal->repeat_pic)
+ // img->duration *= this->nal_parser->current_nal->repeat_pic;
+
+ /* only bt601 and bt701 handled so far. others seem to be rarely used */
+ if(sps->vui_parameters.colour_description_present) {
+ switch (sps->vui_parameters.colour_primaries) {
+ case 1:
+ this->color_standard = VDP_COLOR_STANDARD_ITUR_BT_709;
+ break;
+ case 5:
+ case 6:
+ default:
+ this->color_standard = VDP_COLOR_STANDARD_ITUR_BT_601;
+ break;
+ }
+ }
+
+ this->vdpau_accel->color_standard = this->color_standard;
+
+ struct decoded_picture *decoded_pic = NULL;
+
+
+ uint8_t draw_frame = 0;
+ if (!slc->field_pic_flag) { /* frame coded: simply add to dpb */
+ decoded_pic = init_decoded_picture(this->completed_pic, img);
+ this->completed_pic = NULL;
+ this->dangling_img = NULL;
+
+ dpb_add_picture(this->nal_parser->dpb, decoded_pic, sps->num_ref_frames);
+
+ draw_frame = 1;
+ } else { /* field coded: check for second field */
+ if (!this->incomplete_pic) {
+ decoded_pic = init_decoded_picture(this->completed_pic, img);
+ this->completed_pic = NULL;
+ this->dangling_img = NULL;
+ this->incomplete_pic = decoded_pic;
+ lock_decoded_picture(this->incomplete_pic);
+
+ dpb_add_picture(this->nal_parser->dpb, decoded_pic, sps->num_ref_frames);
+
+ /* don't do a draw yet as the field was incomplete */
+ draw_frame = 0;
+ } else {
+ decoded_pic = this->incomplete_pic;
+ lock_decoded_picture(decoded_pic);
+
+ /* picture is complete now */
+ release_decoded_picture(this->incomplete_pic);
+ this->incomplete_pic = NULL;
+ this->dangling_img = NULL;
+
+ decoded_pic_add_field(decoded_pic, this->completed_pic);
+ this->completed_pic = NULL;
+
+ draw_frame = 1;
+ }
+ }
+
+ release_decoded_picture(decoded_pic);
+
+ /* draw the next frame in display order */
+ if (draw_frame) {
+ draw_frames(this_gen, 0);
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * This function receives a buffer of data from the demuxer layer and
+ * figures out how to handle it based on its header flags.
+ */
+static void vdpau_h264_decode_data (video_decoder_t *this_gen,
+ buf_element_t *buf) {
+
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *) this_gen;
+
+ VdpBitstreamBuffer vdp_buffer;
+ vdp_buffer.struct_version = VDP_BITSTREAM_BUFFER_VERSION;
+
+ /* a video decoder does not care about this flag (?) */
+ if (buf->decoder_flags & BUF_FLAG_PREVIEW)
+ return;
+
+ if(buf->decoder_flags & BUF_FLAG_FRAME_START || buf->decoder_flags & BUF_FLAG_FRAME_END)
+ this->have_frame_boundary_marks = 1;
+
+ if (buf->decoder_flags & BUF_FLAG_FRAMERATE) {
+ this->video_step = buf->decoder_info[0];
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->video_step);
+ }
+
+ if (this->video_step != this->reported_video_step){
+ _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, (this->reported_video_step = this->video_step));
+ }
+
+ if (buf->decoder_flags & BUF_FLAG_STDHEADER) { /* need to initialize */
+ this->have_frame_boundary_marks = 0;
+
+ xine_bmiheader *bih = (xine_bmiheader*)buf->content;
+ this->width = bih->biWidth;
+ this->height = bih->biHeight;
+
+ uint8_t *codec_private = buf->content + sizeof(xine_bmiheader);
+ uint32_t codec_private_len = bih->biSize - sizeof(xine_bmiheader);
+ this->codec_private_len = codec_private_len;
+ this->codec_private = malloc(codec_private_len);
+ memcpy(this->codec_private, codec_private, codec_private_len);
+
+ if(codec_private_len > 0) {
+ parse_codec_private(this->nal_parser, codec_private, codec_private_len);
+ }
+ } else if (buf->decoder_flags & BUF_FLAG_SPECIAL) {
+ this->have_frame_boundary_marks = 0;
+
+ if(buf->decoder_info[1] == BUF_SPECIAL_DECODER_CONFIG) {
+ uint8_t *codec_private = buf->decoder_info_ptr[2];
+ uint32_t codec_private_len = buf->decoder_info[2];
+ this->codec_private_len = codec_private_len;
+ this->codec_private = malloc(codec_private_len);
+ memcpy(this->codec_private, codec_private, codec_private_len);
+
+ if(codec_private_len > 0) {
+ parse_codec_private(this->nal_parser, codec_private, codec_private_len);
+ }
+ } else if (buf->decoder_info[1] == BUF_SPECIAL_PALETTE) {
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ "SPECIAL PALETTE is not yet handled\n");
+ } else
+ xprintf(this->xine, XINE_VERBOSITY_LOG,
+ "UNKNOWN SPECIAL HEADER\n");
+
+ } else {
+ /* parse the first nal packages to retrieve profile type */
+ int len = 0;
+
+ while(len < buf->size && !(this->wait_for_frame_start && !(buf->decoder_flags & BUF_FLAG_FRAME_START))) {
+ this->wait_for_frame_start = 0;
+ len += parse_frame(this->nal_parser, buf->content + len, buf->size - len,
+ buf->pts,
+ (uint8_t**)&vdp_buffer.bitstream, &vdp_buffer.bitstream_bytes, &this->completed_pic);
+
+ if(this->decoder == VDP_INVALID_HANDLE &&
+ this->completed_pic &&
+ this->completed_pic->sps_nal != NULL &&
+ this->completed_pic->sps_nal->sps.pic_width > 0 &&
+ this->completed_pic->sps_nal->sps.pic_height > 0) {
+
+ vdpau_decoder_init(this_gen);
+ }
+
+ if(this->completed_pic &&
+ this->completed_pic->sps_nal != NULL &&
+ this->completed_pic->sps_nal->sps.vui_parameters_present_flag &&
+ this->completed_pic->sps_nal->sps.vui_parameters.bitstream_restriction_flag) {
+
+ this->nal_parser->dpb->max_reorder_frames =
+ this->completed_pic->sps_nal->sps.vui_parameters.num_reorder_frames + 1;
+ this->nal_parser->dpb->max_dpb_frames = this->completed_pic->sps_nal->sps.vui_parameters.max_dec_frame_buffering + 1;
+
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "max reorder count: %d, max dpb count %d\n",
+ this->nal_parser->dpb->max_reorder_frames,
+ this->nal_parser->dpb->max_dpb_frames);
+ }
+
+ if(this->decoder != VDP_INVALID_HANDLE &&
+ vdp_buffer.bitstream_bytes > 0 &&
+ this->completed_pic->slc_nal != NULL &&
+ this->completed_pic->pps_nal != NULL) {
+ vdpau_decoder_render(this_gen, &vdp_buffer, this->completed_pic->slice_cnt);
+ } else if (this->completed_pic != NULL) {
+ free_coded_picture(this->completed_pic);
+ }
+
+ /* in case the last nal was detected as END_OF_SEQUENCE
+ * we will flush the dpb, so that all pictures get drawn
+ */
+ if(this->nal_parser->last_nal_res == 3) {
+ xprintf(this->xine, XINE_VERBOSITY_DEBUG,
+ "END_OF_SEQUENCE, flush buffers\n");
+ vdpau_h264_flush(this_gen);
+ }
+ }
+ }
+
+ if(buf->decoder_flags & BUF_FLAG_FRAME_END)
+ this->wait_for_frame_start = 0;
+}
+
+/*
+ * This function is called when xine needs to flush the system.
+ */
+static void vdpau_h264_flush (video_decoder_t *this_gen) {
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t*) this_gen;
+ struct decoded_picture *decoded_pic = NULL;
+
+ if(this->dangling_img){
+ this->dangling_img->free(this->dangling_img);
+ this->dangling_img = NULL;
+ }
+
+ if (this->incomplete_pic) {
+ release_decoded_picture(this->incomplete_pic);
+ this->incomplete_pic = NULL;
+ }
+
+ draw_frames(this_gen, 1);
+ dpb_free_all(this->nal_parser->dpb);
+ this->reset = VO_NEW_SEQUENCE_FLAG;
+}
+
+/*
+ * This function resets the video decoder.
+ */
+static void vdpau_h264_reset (video_decoder_t *this_gen) {
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *) this_gen;
+
+ dpb_free_all(this->nal_parser->dpb);
+
+ if (this->decoder != VDP_INVALID_HANDLE) {
+ this->vdpau_accel->vdp_decoder_destroy( this->decoder );
+ this->decoder = VDP_INVALID_HANDLE;
+ }
+
+ // Doing a full parser reinit here works more reliable than
+ // resetting
+
+ //reset_parser(this->nal_parser);
+ free_parser(this->nal_parser);
+ this->nal_parser = init_parser(this->xine);
+
+ this->color_standard = VDP_COLOR_STANDARD_ITUR_BT_601;
+ this->video_step = 0;
+
+ if(this->codec_private_len > 0) {
+ parse_codec_private(this->nal_parser, this->codec_private, this->codec_private_len);
+
+ /* if the stream does not contain frame boundary marks we
+ * have to hope that the next nal will start with the next
+ * incoming buf... seems to work, though...
+ */
+ this->wait_for_frame_start = this->have_frame_boundary_marks;
+ }
+
+ if (this->incomplete_pic) {
+ release_decoded_picture(this->incomplete_pic);
+ this->incomplete_pic = NULL;
+ }
+
+ if (this->dangling_img) {
+ this->dangling_img->free(this->dangling_img);
+ this->dangling_img = NULL;
+ }
+
+ this->progressive_cnt = 0;
+ this->reset = VO_NEW_SEQUENCE_FLAG;
+}
+
+/*
+ * The decoder should forget any stored pts values here.
+ */
+static void vdpau_h264_discontinuity (video_decoder_t *this_gen) {
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *) this_gen;
+
+ dpb_clear_all_pts(this->nal_parser->dpb);
+ this->reset = VO_NEW_SEQUENCE_FLAG;
+}
+
+/*
+ * This function frees the video decoder instance allocated to the decoder.
+ */
+static void vdpau_h264_dispose (video_decoder_t *this_gen) {
+
+ vdpau_h264_decoder_t *this = (vdpau_h264_decoder_t *) this_gen;
+
+ if (this->incomplete_pic) {
+ release_decoded_picture(this->incomplete_pic);
+ this->incomplete_pic = NULL;
+ }
+
+ if (this->dangling_img) {
+ this->dangling_img->free(this->dangling_img);
+ this->dangling_img = NULL;
+ }
+
+ dpb_free_all(this->nal_parser->dpb);
+
+ if (this->decoder != VDP_INVALID_HANDLE) {
+ this->vdpau_accel->vdp_decoder_destroy( this->decoder );
+ this->decoder = VDP_INVALID_HANDLE;
+ }
+
+ this->stream->video_out->close( this->stream->video_out, this->stream );
+
+ free_parser (this->nal_parser);
+ free (this_gen);
+}
+
+/*
+ * This function allocates, initializes, and returns a private video
+ * decoder structure.
+ */
+static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) {
+
+ vdpau_h264_decoder_t *this ;
+
+ /* the videoout must be vdpau-capable to support this decoder */
+ if ( !(stream->video_driver->get_capabilities(stream->video_driver) & VO_CAP_VDPAU_H264) )
+ return NULL;
+
+ /* now check if vdpau has free decoder resource */
+ vo_frame_t *img = stream->video_out->get_frame( stream->video_out, 1920, 1080, 1, XINE_IMGFMT_VDPAU, VO_BOTH_FIELDS );
+ vdpau_accel_t *accel = (vdpau_accel_t*)img->accel_data;
+ int runtime_nr = accel->vdp_runtime_nr;
+ img->free(img);
+ VdpDecoder decoder;
+ VdpStatus st = accel->vdp_decoder_create( accel->vdp_device, VDP_DECODER_PROFILE_H264_MAIN, 1920, 1080, 16, &decoder );
+ if ( st!=VDP_STATUS_OK ) {
+ lprintf( "can't create vdpau decoder.\n" );
+ return NULL;
+ }
+
+ accel->vdp_decoder_destroy( decoder );
+
+ this = (vdpau_h264_decoder_t *) calloc(1, sizeof(vdpau_h264_decoder_t));
+
+ this->nal_parser = init_parser(stream->xine);
+
+ this->video_decoder.decode_data = vdpau_h264_decode_data;
+ this->video_decoder.flush = vdpau_h264_flush;
+ this->video_decoder.reset = vdpau_h264_reset;
+ this->video_decoder.discontinuity = vdpau_h264_discontinuity;
+ this->video_decoder.dispose = vdpau_h264_dispose;
+
+ this->stream = stream;
+ this->xine = stream->xine;
+ this->class = (vdpau_h264_class_t *) class_gen;
+
+ this->decoder = VDP_INVALID_HANDLE;
+ this->vdp_runtime_nr = runtime_nr;
+ this->color_standard = VDP_COLOR_STANDARD_ITUR_BT_601;
+ this->progressive_cnt = 0;
+
+ this->reset = VO_NEW_SEQUENCE_FLAG;
+
+ (this->stream->video_out->open) (this->stream->video_out, this->stream);
+
+ return &this->video_decoder;
+}
+
+/*
+ * This function allocates a private video decoder class and initializes
+ * the class's member functions.
+ */
+static void *init_plugin (xine_t *xine, void *data) {
+
+ vdpau_h264_class_t *this;
+
+ this = (vdpau_h264_class_t *) calloc(1, sizeof(vdpau_h264_class_t));
+
+ this->decoder_class.open_plugin = open_plugin;
+ this->decoder_class.identifier = "vdpau_h264";
+ this->decoder_class.description =
+ N_("vdpau_h264: h264 decoder plugin using VDPAU hardware decoding.\n"
+ "Must be used along with video_out_vdpau.");
+ this->decoder_class.dispose = default_video_decoder_class_dispose;
+
+ return this;
+}
+
+/*
+ * This is a list of all of the internal xine video buffer types that
+ * this decoder is able to handle. Check src/xine-engine/buffer.h for a
+ * list of valid buffer types (and add a new one if the one you need does
+ * not exist). Terminate the list with a 0.
+ */
+static const uint32_t video_types[] = {
+ /* BUF_VIDEO_FOOVIDEO, */
+ BUF_VIDEO_H264,
+ 0
+};
+
+/*
+ * This data structure combines the list of supported xine buffer types and
+ * the priority that the plugin should be given with respect to other
+ * plugins that handle the same buffer type. A plugin with priority (n+1)
+ * will be used instead of a plugin with priority (n).
+ */
+static const decoder_info_t dec_info_video = {
+ video_types, /* supported types */
+ 7 /* priority */
+};
+
+/*
+ * The plugin catalog entry. This is the only information that this plugin
+ * will export to the public.
+ */
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* { type, API, "name", version, special_info, init_function } */
+ { PLUGIN_VIDEO_DECODER | PLUGIN_MUST_PRELOAD, 19, "vdpau_h264", XINE_VERSION_CODE, &dec_info_video, init_plugin },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};