summaryrefslogtreecommitdiff
path: root/src/video_dec/libvdpau/dpb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_dec/libvdpau/dpb.c')
-rw-r--r--src/video_dec/libvdpau/dpb.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/src/video_dec/libvdpau/dpb.c b/src/video_dec/libvdpau/dpb.c
new file mode 100644
index 000000000..60a755013
--- /dev/null
+++ b/src/video_dec/libvdpau/dpb.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) 2008 Julian Scheel
+ *
+ * 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
+ *
+ * dpb.c: Implementing Decoded Picture Buffer
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpb.h"
+#include "dpb.h"
+#include "nal.h"
+
+#include "h264_parser.h"
+
+#include "accel_vdpau.h"
+
+#include <xine/video_out.h>
+
+//#define DEBUG_DPB
+
+int dp_top_field_first(struct decoded_picture *decoded_pic)
+{
+ int top_field_first = 1;
+
+
+ if (decoded_pic->coded_pic[1] != NULL) {
+ if (!decoded_pic->coded_pic[0]->slc_nal->slc.bottom_field_flag &&
+ decoded_pic->coded_pic[1]->slc_nal->slc.bottom_field_flag &&
+ decoded_pic->coded_pic[0]->top_field_order_cnt !=
+ decoded_pic->coded_pic[1]->bottom_field_order_cnt) {
+ top_field_first = decoded_pic->coded_pic[0]->top_field_order_cnt < decoded_pic->coded_pic[1]->bottom_field_order_cnt;
+ } else if (decoded_pic->coded_pic[0]->slc_nal->slc.bottom_field_flag &&
+ !decoded_pic->coded_pic[1]->slc_nal->slc.bottom_field_flag &&
+ decoded_pic->coded_pic[0]->bottom_field_order_cnt !=
+ decoded_pic->coded_pic[1]->top_field_order_cnt) {
+ top_field_first = decoded_pic->coded_pic[0]->bottom_field_order_cnt > decoded_pic->coded_pic[1]->top_field_order_cnt;
+ }
+ }
+
+ if (decoded_pic->coded_pic[0]->flag_mask & PIC_STRUCT_PRESENT && decoded_pic->coded_pic[0]->sei_nal != NULL) {
+ uint8_t pic_struct = decoded_pic->coded_pic[0]->sei_nal->sei.pic_timing.pic_struct;
+ if(pic_struct == DISP_TOP_BOTTOM ||
+ pic_struct == DISP_TOP_BOTTOM_TOP) {
+ top_field_first = 1;
+ } else if (pic_struct == DISP_BOTTOM_TOP ||
+ pic_struct == DISP_BOTTOM_TOP_BOTTOM) {
+ top_field_first = 0;
+ } else if (pic_struct == DISP_FRAME) {
+ top_field_first = 1;
+ }
+ }
+
+ return top_field_first;
+}
+
+/**
+ * ----------------------------------------------------------------------------
+ * decoded picture
+ * ----------------------------------------------------------------------------
+ */
+
+void free_decoded_picture(struct decoded_picture *pic);
+
+struct decoded_picture* init_decoded_picture(struct coded_picture *cpic, vo_frame_t *img)
+{
+ struct decoded_picture *pic = calloc(1, sizeof(struct decoded_picture));
+
+ pic->coded_pic[0] = cpic;
+
+ decoded_pic_check_reference(pic);
+ pic->img = img;
+ pic->lock_counter = 1;
+
+ return pic;
+}
+
+void decoded_pic_check_reference(struct decoded_picture *pic)
+{
+ int i;
+ for(i = 0; i < 2; i++) {
+ struct coded_picture *cpic = pic->coded_pic[i];
+ if(cpic && (cpic->flag_mask & REFERENCE)) {
+ // FIXME: this assumes Top Field First!
+ if(i == 0) {
+ pic->top_is_reference = cpic->slc_nal->slc.field_pic_flag
+ ? (cpic->slc_nal->slc.bottom_field_flag ? 0 : 1) : 1;
+ }
+
+ pic->bottom_is_reference = cpic->slc_nal->slc.field_pic_flag
+ ? (cpic->slc_nal->slc.bottom_field_flag ? 1 : 0) : 1;
+ }
+ }
+}
+
+void decoded_pic_add_field(struct decoded_picture *pic,
+ struct coded_picture *cpic)
+{
+ pic->coded_pic[1] = cpic;
+
+ decoded_pic_check_reference(pic);
+}
+
+void release_decoded_picture(struct decoded_picture *pic)
+{
+ if(!pic)
+ return;
+
+ pic->lock_counter--;
+ //printf("release decoded picture: %p (%d)\n", pic, pic->lock_counter);
+
+ if(pic->lock_counter <= 0) {
+ free_decoded_picture(pic);
+ }
+}
+
+void lock_decoded_picture(struct decoded_picture *pic)
+{
+ if(!pic)
+ return;
+
+ pic->lock_counter++;
+ //printf("lock decoded picture: %p (%d)\n", pic, pic->lock_counter);
+}
+
+void free_decoded_picture(struct decoded_picture *pic)
+{
+ if(!pic)
+ return;
+
+ if(pic->img != NULL) {
+ pic->img->free(pic->img);
+ }
+
+ free_coded_picture(pic->coded_pic[1]);
+ free_coded_picture(pic->coded_pic[0]);
+ pic->coded_pic[0] = NULL;
+ pic->coded_pic[1] = NULL;
+ free(pic);
+}
+
+
+
+
+/**
+ * ----------------------------------------------------------------------------
+ * dpb code starting here
+ * ----------------------------------------------------------------------------
+ */
+
+struct dpb* create_dpb()
+{
+ struct dpb *dpb = calloc(1, sizeof(struct dpb));
+
+ dpb->output_list = xine_list_new();
+ dpb->reference_list = xine_list_new();
+
+ dpb->max_reorder_frames = MAX_DPB_COUNT;
+ dpb->max_dpb_frames = MAX_DPB_COUNT;
+
+ return dpb;
+}
+
+int dpb_total_frames(struct dpb *dpb)
+{
+ int num_frames = xine_list_size(dpb->output_list);
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while(ite) {
+ struct decoded_picture *pic = xine_list_get_value(dpb->reference_list, ite);
+ if (xine_list_find(dpb->output_list, pic) == NULL) {
+ num_frames++;
+ }
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return num_frames;
+}
+
+void release_dpb(struct dpb *dpb)
+{
+ if(!dpb)
+ return;
+
+ dpb_free_all(dpb);
+
+ xine_list_delete(dpb->output_list);
+ xine_list_delete(dpb->reference_list);
+
+ free(dpb);
+}
+
+struct decoded_picture* dpb_get_next_out_picture(struct dpb *dpb, int do_flush)
+{
+ struct decoded_picture *pic = NULL;;
+ struct decoded_picture *outpic = NULL;
+
+ if(!do_flush &&
+ xine_list_size(dpb->output_list) < dpb->max_reorder_frames &&
+ dpb_total_frames(dpb) < dpb->max_dpb_frames) {
+ return NULL;
+ }
+
+ xine_list_iterator_t ite = xine_list_back(dpb->output_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->output_list, ite);
+
+ int32_t out_top_field_order_cnt = outpic != NULL ?
+ outpic->coded_pic[0]->top_field_order_cnt : 0;
+ int32_t top_field_order_cnt = pic->coded_pic[0]->top_field_order_cnt;
+
+ int32_t out_bottom_field_order_cnt = outpic != NULL ?
+ (outpic->coded_pic[1] != NULL ?
+ outpic->coded_pic[1]->bottom_field_order_cnt :
+ outpic->coded_pic[0]->top_field_order_cnt) : 0;
+ int32_t bottom_field_order_cnt = pic->coded_pic[1] != NULL ?
+ pic->coded_pic[1]->bottom_field_order_cnt :
+ pic->coded_pic[0]->top_field_order_cnt;
+
+ if (outpic == NULL ||
+ (top_field_order_cnt <= out_top_field_order_cnt &&
+ bottom_field_order_cnt <= out_bottom_field_order_cnt) ||
+ (out_top_field_order_cnt <= 0 && top_field_order_cnt > 0 &&
+ out_bottom_field_order_cnt <= 0 && bottom_field_order_cnt > 0) ||
+ outpic->coded_pic[0]->flag_mask & IDR_PIC) {
+ outpic = pic;
+ }
+
+ ite = xine_list_prev(dpb->output_list, ite);
+ }
+
+ return outpic;
+}
+
+struct decoded_picture* dpb_get_picture(struct dpb *dpb, uint32_t picnum)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ if ((pic->coded_pic[0]->pic_num == picnum ||
+ (pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->pic_num == picnum))) {
+ return pic;
+ }
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return NULL;
+}
+
+struct decoded_picture* dpb_get_picture_by_ltpn(struct dpb *dpb,
+ uint32_t longterm_picnum)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ if (pic->coded_pic[0]->long_term_pic_num == longterm_picnum ||
+ (pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->long_term_pic_num == longterm_picnum)) {
+ return pic;
+ }
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return NULL;
+}
+
+struct decoded_picture* dpb_get_picture_by_ltidx(struct dpb *dpb,
+ uint32_t longterm_idx)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ if (pic->coded_pic[0]->long_term_frame_idx == longterm_idx ||
+ (pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->long_term_frame_idx == longterm_idx)) {
+ return pic;
+ }
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return NULL;
+}
+
+int dpb_set_unused_ref_picture_byltpn(struct dpb *dpb, uint32_t longterm_picnum)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ uint8_t found = 0;
+
+ if (pic->coded_pic[0]->long_term_pic_num == longterm_picnum) {
+ pic->coded_pic[0]->used_for_long_term_ref = 0;
+ found = 1;
+ }
+
+ if ((pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->long_term_pic_num == longterm_picnum)) {
+ pic->coded_pic[1]->used_for_long_term_ref = 0;
+ found = 1;
+ }
+
+ if(found && !pic->coded_pic[0]->used_for_long_term_ref &&
+ (pic->coded_pic[1] == NULL ||
+ !pic->coded_pic[1]->used_for_long_term_ref)) {
+ dpb_unmark_reference_picture(dpb, pic);
+ }
+
+ if (found)
+ return 0;
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return -1;
+}
+
+int dpb_set_unused_ref_picture_bylidx(struct dpb *dpb, uint32_t longterm_idx)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ uint8_t found = 0;
+
+ if (pic->coded_pic[0]->long_term_frame_idx == longterm_idx) {
+ pic->coded_pic[0]->used_for_long_term_ref = 0;
+ found = 1;
+ }
+
+ if ((pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->long_term_frame_idx == longterm_idx)) {
+ pic->coded_pic[1]->used_for_long_term_ref = 0;
+ found = 1;
+ }
+
+ if(found && !pic->coded_pic[0]->used_for_long_term_ref &&
+ (pic->coded_pic[1] == NULL ||
+ !pic->coded_pic[1]->used_for_long_term_ref)) {
+ dpb_unmark_reference_picture(dpb, pic);
+ }
+
+ if (found)
+ return 0;
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return -1;
+}
+
+int dpb_set_unused_ref_picture_lidx_gt(struct dpb *dpb, int32_t longterm_idx)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ uint8_t found = 0;
+
+ if (pic->coded_pic[0]->long_term_frame_idx >= longterm_idx) {
+ pic->coded_pic[0]->used_for_long_term_ref = 0;
+ found = 1;
+ }
+
+ if ((pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->long_term_frame_idx >= longterm_idx)) {
+ pic->coded_pic[1]->used_for_long_term_ref = 0;
+ found = 1;
+ }
+
+ if(found && !pic->coded_pic[0]->used_for_long_term_ref &&
+ (pic->coded_pic[1] == NULL ||
+ !pic->coded_pic[1]->used_for_long_term_ref)) {
+ dpb_unmark_reference_picture(dpb, pic);
+ }
+
+ ite = xine_list_next(dpb->reference_list, ite);
+ }
+
+ return -1;
+}
+
+
+int dpb_unmark_picture_delayed(struct dpb *dpb, struct decoded_picture *pic)
+{
+ if(!pic)
+ return -1;
+
+ xine_list_iterator_t ite = xine_list_find(dpb->output_list, pic);
+ if (ite) {
+ xine_list_remove(dpb->output_list, ite);
+ release_decoded_picture(pic);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int dpb_unmark_reference_picture(struct dpb *dpb, struct decoded_picture *pic)
+{
+ if(!pic)
+ return -1;
+
+ xine_list_iterator_t ite = xine_list_find(dpb->reference_list, pic);
+ if (ite) {
+ xine_list_remove(dpb->reference_list, ite);
+ release_decoded_picture(pic);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+/*static int dpb_remove_picture_by_img(struct dpb *dpb, vo_frame_t *remimg)
+{
+ int retval = -1;
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->output_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->output_list, ite);
+
+ if (pic->img == remimg) {
+ dpb_unmark_picture_delayed(dpb, pic);
+ dpb->used--;
+ retval = 0;
+ }
+
+ ite = xine_list_next(dpb->output_list, ite);
+ }
+
+ return retval;
+}*/
+
+
+int dpb_add_picture(struct dpb *dpb, struct decoded_picture *pic, uint32_t num_ref_frames)
+{
+#if 0
+ /* this should never happen */
+ pic->img->lock(pic->img);
+ if (0 == dpb_remove_picture_by_img(dpb, pic->img))
+ lprintf("H264/DPB broken stream: current img was already in dpb -- freed it\n");
+ else
+ pic->img->free(pic->img);
+#endif
+
+ /* add the pic to the output picture list, as no
+ * pic would be immediately drawn.
+ * acquire a lock for this list
+ */
+ lock_decoded_picture(pic);
+ xine_list_push_back(dpb->output_list, pic);
+
+
+ /* check if the pic is a reference pic,
+ * if it is it should be added to the reference
+ * list. another lock has to be acquired in that case
+ */
+ if (pic->coded_pic[0]->flag_mask & REFERENCE ||
+ (pic->coded_pic[1] != NULL &&
+ pic->coded_pic[1]->flag_mask & REFERENCE)) {
+ lock_decoded_picture(pic);
+ xine_list_push_back(dpb->reference_list, pic);
+
+ /*
+ * always apply the sliding window reference removal, if more reference
+ * frames than expected are in the list. we will always remove the oldest
+ * reference frame
+ */
+ if(xine_list_size(dpb->reference_list) > num_ref_frames) {
+ struct decoded_picture *discard = xine_list_get_value(dpb->reference_list, xine_list_front(dpb->reference_list));
+ dpb_unmark_reference_picture(dpb, discard);
+ }
+ }
+
+#if DEBUG_DPB
+ printf("DPB list sizes: Total: %2d, Output: %2d, Reference: %2d\n",
+ dpb_total_frames(dpb), xine_list_size(dpb->output_list),
+ xine_list_size(dpb->reference_list));
+#endif
+
+ return 0;
+}
+
+int dpb_flush(struct dpb *dpb)
+{
+ struct decoded_picture *pic = NULL;
+
+ xine_list_iterator_t ite = xine_list_front(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+
+ dpb_unmark_reference_picture(dpb, pic);
+
+ /* CAUTION: xine_list_next would return an item, but not the one we
+ * expect, as the current one was deleted
+ */
+ ite = xine_list_front(dpb->reference_list);
+ }
+
+ return 0;
+}
+
+void dpb_free_all(struct dpb *dpb)
+{
+ xine_list_iterator_t ite = xine_list_front(dpb->output_list);
+ while(ite) {
+ dpb_unmark_picture_delayed(dpb, xine_list_get_value(dpb->output_list, ite));
+ /* CAUTION: xine_list_next would return an item, but not the one we
+ * expect, as the current one was deleted
+ */
+ ite = xine_list_front(dpb->output_list);
+ }
+
+ ite = xine_list_front(dpb->reference_list);
+ while(ite) {
+ dpb_unmark_reference_picture(dpb, xine_list_get_value(dpb->reference_list, ite));
+ /* CAUTION: xine_list_next would return an item, but not the one we
+ * expect, as the current one was deleted
+ */
+ ite = xine_list_front(dpb->reference_list);
+ }
+}
+
+void dpb_clear_all_pts(struct dpb *dpb)
+{
+ xine_list_iterator_t ite = xine_list_front(dpb->output_list);
+ while(ite) {
+ struct decoded_picture *pic = xine_list_get_value(dpb->output_list, ite);
+ pic->img->pts = 0;
+
+ ite = xine_list_next(dpb->output_list, ite);
+ }
+}
+
+int fill_vdpau_reference_list(struct dpb *dpb, VdpReferenceFrameH264 *reflist)
+{
+ struct decoded_picture *pic = NULL;
+
+ int i = 0;
+ int used_refframes = 0;
+
+ xine_list_iterator_t ite = xine_list_back(dpb->reference_list);
+ while (ite) {
+ pic = xine_list_get_value(dpb->reference_list, ite);
+ reflist[i].surface = ((vdpau_accel_t*)pic->img->accel_data)->surface;
+ reflist[i].is_long_term = pic->coded_pic[0]->used_for_long_term_ref ||
+ (pic->coded_pic[1] != NULL && pic->coded_pic[1]->used_for_long_term_ref);
+
+ reflist[i].frame_idx = pic->coded_pic[0]->used_for_long_term_ref ?
+ pic->coded_pic[0]->long_term_pic_num :
+ pic->coded_pic[0]->slc_nal->slc.frame_num;
+ reflist[i].top_is_reference = pic->top_is_reference;
+ reflist[i].bottom_is_reference = pic->bottom_is_reference;
+ reflist[i].field_order_cnt[0] = pic->coded_pic[0]->top_field_order_cnt;
+ reflist[i].field_order_cnt[1] = pic->coded_pic[1] != NULL ?
+ pic->coded_pic[1]->bottom_field_order_cnt :
+ pic->coded_pic[0]->bottom_field_order_cnt;
+ i++;
+
+ ite = xine_list_prev(dpb->reference_list, ite);
+ }
+
+ used_refframes = i;
+
+ // fill all other frames with invalid handles
+ while(i < 16) {
+ reflist[i].bottom_is_reference = VDP_FALSE;
+ reflist[i].top_is_reference = VDP_FALSE;
+ reflist[i].frame_idx = 0;
+ reflist[i].is_long_term = VDP_FALSE;
+ reflist[i].surface = VDP_INVALID_HANDLE;
+ reflist[i].field_order_cnt[0] = 0;
+ reflist[i].field_order_cnt[1] = 0;
+ i++;
+ }
+
+ return used_refframes;
+}