diff options
Diffstat (limited to 'src/video_dec/libmpeg2new/xine_mpeg2new_decoder.c')
-rw-r--r-- | src/video_dec/libmpeg2new/xine_mpeg2new_decoder.c | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/src/video_dec/libmpeg2new/xine_mpeg2new_decoder.c b/src/video_dec/libmpeg2new/xine_mpeg2new_decoder.c new file mode 100644 index 000000000..2678168e2 --- /dev/null +++ b/src/video_dec/libmpeg2new/xine_mpeg2new_decoder.c @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2000-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * stuff needed to turn libmpeg2 into a xine decoder plugin + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <inttypes.h> +#include <assert.h> + +#include "./include/mpeg2.h" +#include <xine/xine_internal.h> +#include <xine/video_out.h> +#include <xine/buffer.h> + +/* +#define LOG +#define LOG_FRAME_ALLOC_FREE +#define LOG_ENTRY +#define LOG_FRAME_COUNTER +*/ + +#define _x_abort() do {} while (0) + +typedef struct { + video_decoder_class_t decoder_class; +} mpeg2_class_t; + +typedef struct { + uint32_t id; + vo_frame_t * img; +} img_state_t; + +typedef struct mpeg2_video_decoder_s { + video_decoder_t video_decoder; + mpeg2dec_t *mpeg2dec; + mpeg2_class_t *class; + xine_stream_t *stream; + int32_t force_aspect; + int force_pan_scan; + double ratio; + img_state_t img_state[30]; + uint32_t frame_number; + uint32_t rff_pattern; + +} mpeg2_video_decoder_t; + +#ifndef LOG_FRAME_ALLOC_FREE +inline static void mpeg2_video_print_bad_state(img_state_t * img_state) {} +#else +static void mpeg2_video_print_bad_state(img_state_t * img_state) { + int32_t n,m; + m=0; + for(n=0;n<30;n++) { + if (img_state[n].id>0) { + printf("%d = %u\n",n, img_state[n].id); + m++; + } + } + if (m > 3) _x_abort(); + if (m == 0) printf("NO FRAMES\n"); +} +#endif + +static void mpeg2_video_free_all(img_state_t * img_state) { + int32_t n,m; + vo_frame_t * img; + printf("libmpeg2new:free_all\n"); + for(n=0;n<30;n++) { + if (img_state[n].id>0) { + img = img_state[n].img; + img->free(img); + img_state[n].id = 0; + } + } +} + + +static void mpeg2_video_print_fbuf(const mpeg2_fbuf_t * fbuf) { + printf("%p",fbuf); + vo_frame_t * img; + if (fbuf) { + img = (vo_frame_t *) fbuf->id; + if (img) { + printf (", img=%p, (id=%d)\n", + img, img->id); + } else { + printf (", img=NULL\n"); + } + } else { + printf ("\n"); + } +} + +static void mpeg2_video_decode_data (video_decoder_t *this_gen, buf_element_t *buf_element) { + mpeg2_video_decoder_t *this = (mpeg2_video_decoder_t *) this_gen; + uint8_t * current = buf_element->content; + uint8_t * end = buf_element->content + buf_element->size; + const mpeg2_info_t * info; + mpeg2_state_t state; + vo_frame_t * img; + uint32_t picture_structure; + int32_t frame_skipping; + + /* handle aspect hints from xine-dvdnav */ + if (buf_element->decoder_flags & BUF_FLAG_SPECIAL) { + if (buf_element->decoder_info[1] == BUF_SPECIAL_ASPECT) { + this->force_aspect = buf_element->decoder_info[2]; + if (buf_element->decoder_info[3] == 0x1 && buf_element->decoder_info[2] == 3) + /* letterboxing is denied, we have to do pan&scan */ + this->force_pan_scan = 1; + else + this->force_pan_scan = 0; + } + + return; + } + + if (buf_element->decoder_flags != 0) return; + +#ifdef LOG_ENTRY + printf ("libmpeg2: decode_data: enter\n"); +#endif + + mpeg2_buffer (this->mpeg2dec, current, end); + + info = mpeg2_info (this->mpeg2dec); + + while ((state = mpeg2_parse (this->mpeg2dec)) != STATE_BUFFER) { + switch (state) { + case STATE_SEQUENCE: + /* might set nb fbuf, convert format, stride */ + /* might set fbufs */ + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_BITRATE, info->sequence->byte_rate * 8); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, info->sequence->picture_width); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, info->sequence->picture_height); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, info->sequence->frame_period / 300); + if (this->force_aspect) ((mpeg2_sequence_t *)info->sequence)->pixel_width = this->force_aspect; /* ugly... */ + switch (info->sequence->pixel_width) { + case 3: + this->ratio = 16.0 / 9.0; + break; + case 4: + this->ratio = 2.11; + break; + case 2: + this->ratio = 4.0 / 3.0; + break; + case 1: + default: + this->ratio = (double)info->sequence->picture_width/(double)info->sequence->picture_height; + break; + } + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_RATIO, (int)(10000*this->ratio)); + + if (info->sequence->flags & SEQ_FLAG_MPEG2) { + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "MPEG 2 (libmpeg2new)"); + } else { + _x_meta_info_set_utf8(this->stream, XINE_META_INFO_VIDEOCODEC, "MPEG 1 (libmpeg2new)"); + } + + break; + case STATE_PICTURE: + /* might skip */ + /* might set fbuf */ + if (info->current_picture->nb_fields == 1) { + picture_structure = info->current_picture->flags & PIC_FLAG_TOP_FIELD_FIRST ? VO_TOP_FIELD : VO_BOTTOM_FIELD; + } else { + picture_structure = VO_BOTH_FIELDS; + } + + img = this->stream->video_out->get_frame (this->stream->video_out, + info->sequence->picture_width, + info->sequence->picture_height, + this->ratio, + XINE_IMGFMT_YV12, + picture_structure); + this->frame_number++; +#ifdef LOG_FRAME_COUNTER + printf("libmpeg2:frame_number=%d\n",this->frame_number); +#endif + img->top_field_first = info->current_picture->flags & PIC_FLAG_TOP_FIELD_FIRST ? 1 : 0; + img->repeat_first_field = (info->current_picture->nb_fields > 2) ? 1 : 0; + img->duration=info->sequence->frame_period / 300; + if( ((this->rff_pattern & 0xff) == 0xaa || + (this->rff_pattern & 0xff) == 0x55) ) { + /* special case for ntsc 3:2 pulldown */ + img->duration += img->duration/4; + } else { + if( img->repeat_first_field ) { + img->duration = (img->duration * info->current_picture->nb_fields) / 2; + } + } + + if ((info->current_picture->flags & 7) == 1) { + img->pts=buf_element->pts; /* If an I frame, use PTS */ + } else { + img->pts=0; + } + + +#ifdef LOG_FRAME_ALLOC_FREE + printf ("libmpeg2:decode_data:get_frame xine=%p (id=%d)\n", img,img->id); +#endif + if (this->img_state[img->id].id != 0) { + printf ("libmpeg2:decode_data:get_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id].id); + _x_abort(); + } + + this->img_state[img->id].id = 1; + this->img_state[img->id].img = img; + + mpeg2_set_buf (this->mpeg2dec, img->base, img); + break; + case STATE_SLICE: + case STATE_END: +#if 0 + printf("libmpeg2:decode_data:current_fbuf="); + mpeg2_video_print_fbuf(info->current_fbuf); + printf("libmpeg2:decode_data:display_fbuf="); + mpeg2_video_print_fbuf(info->display_fbuf); + printf("libmpeg2:decode_data:discard_fbuf="); + mpeg2_video_print_fbuf(info->discard_fbuf); +#endif + /* draw current picture */ + /* might free frame buffer */ + if (info->display_fbuf && info->display_fbuf->id) { + img = (vo_frame_t *) info->display_fbuf->id; + /* this should be used to detect any special rff pattern */ + this->rff_pattern = this->rff_pattern << 1; + this->rff_pattern |= img->repeat_first_field; + +#ifdef LOG_FRAME_ALLOC_FREE + printf ("libmpeg2:decode_data:draw_frame xine=%p, fbuf=%p, id=%d \n", img, info->display_fbuf, img->id); +#endif + if (this->img_state[img->id].id != 1) { + printf ("libmpeg2:decode_data:draw_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id].id); + _x_abort(); + } + if (this->img_state[img->id].id == 1) { + frame_skipping = img->draw (img, this->stream); + /* FIXME: Handle skipping */ + this->img_state[img->id].id = 2; + } + + } + if (info->discard_fbuf && !info->discard_fbuf->id) { + printf ("libmpeg2:decode_data:BAD free_frame discard: xine=%p, fbuf=%p\n", info->discard_fbuf->id, info->discard_fbuf); + //_x_abort(); + } + if (info->discard_fbuf && info->discard_fbuf->id) { + img = (vo_frame_t *) info->discard_fbuf->id; +#ifdef LOG_FRAME_ALLOC_FREE + printf ("libmpeg2:decode_data:free_frame xine=%p, fbuf=%p,id=%d\n", img, info->discard_fbuf, img->id); +#endif + if (this->img_state[img->id].id != 2) { + printf ("libmpeg2:decode_data:free_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id].id); + _x_abort(); + } + if (this->img_state[img->id].id == 2) { + img->free(img); + this->img_state[img->id].id = 0; + } + } +#ifdef LOG_FRAME_ALLOC_FREE + mpeg2_video_print_bad_state(this->img_state); +#endif + break; + case STATE_GOP: + break; + default: + printf("libmpeg2new: STATE unknown %d\n",state); + break; + } + + } +#ifdef LOG_ENTRY + printf ("libmpeg2: decode_data: exit\n"); +#endif + +} + +static void mpeg2_video_flush (video_decoder_t *this_gen) { + mpeg2_video_decoder_t *this = (mpeg2_video_decoder_t *) this_gen; + +#ifdef LOG_ENTRY + printf ("libmpeg2: flush\n"); +#endif + +/* mpeg2_flush (&this->mpeg2); */ +} + +static void mpeg2_video_reset (video_decoder_t *this_gen) { + mpeg2_video_decoder_t *this = (mpeg2_video_decoder_t *) this_gen; + int32_t state; + const mpeg2_info_t * info; + vo_frame_t * img; + int32_t frame_skipping; + +#ifdef LOG_ENTRY + printf ("libmpeg2: reset\n"); +#endif + mpeg2_reset (this->mpeg2dec, 1); /* 1 for full reset */ + mpeg2_video_free_all(this->img_state); + + +#if 0 /* This bit of code does not work yet. */ + info = mpeg2_info (this->mpeg2dec); + state = mpeg2_reset (this->mpeg2dec); + printf("reset state1:%d\n",state); + if (info->display_fbuf && info->display_fbuf->id) { + img = (vo_frame_t *) info->display_fbuf->id; + + if (this->img_state[img->id] != 1) { + printf ("libmpeg2:decode_data:draw_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); + _x_abort(); + } + if (this->img_state[img->id] == 1) { + frame_skipping = img->draw (img, this->stream); + /* FIXME: Handle skipping */ + this->img_state[img->id] = 2; + } + } + + if (info->discard_fbuf && !info->discard_fbuf->id) { + printf ("libmpeg2:decode_data:BAD free_frame discard_fbuf=%p\n", info->discard_fbuf); + _x_abort(); + } + if (info->discard_fbuf && info->discard_fbuf->id) { + img = (vo_frame_t *) info->discard_fbuf->id; + if (this->img_state[img->id] != 2) { + printf ("libmpeg2:decode_data:free_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); + _x_abort(); + } + if (this->img_state[img->id] == 2) { + img->free(img); + this->img_state[img->id] = 0; + } + } + state = mpeg2_parse (this->mpeg2dec); + printf("reset state2:%d\n",state); + if (info->display_fbuf && info->display_fbuf->id) { + img = (vo_frame_t *) info->display_fbuf->id; + + if (this->img_state[img->id] != 1) { + printf ("libmpeg2:decode_data:draw_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); + _x_abort(); + } + if (this->img_state[img->id] == 1) { + frame_skipping = img->draw (img, this->stream); + /* FIXME: Handle skipping */ + this->img_state[img->id] = 2; + } + } + + if (info->discard_fbuf && !info->discard_fbuf->id) { + printf ("libmpeg2:decode_data:BAD free_frame discard_fbuf=%p\n", info->discard_fbuf); + _x_abort(); + } + if (info->discard_fbuf && info->discard_fbuf->id) { + img = (vo_frame_t *) info->discard_fbuf->id; + if (this->img_state[img->id] != 2) { + printf ("libmpeg2:decode_data:free_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); + _x_abort(); + } + if (this->img_state[img->id] == 2) { + img->free(img); + this->img_state[img->id] = 0; + } + } + state = mpeg2_parse (this->mpeg2dec); + printf("reset state3:%d\n",state); + if (info->display_fbuf && info->display_fbuf->id) { + img = (vo_frame_t *) info->display_fbuf->id; + + if (this->img_state[img->id] != 1) { + printf ("libmpeg2:decode_data:draw_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); + _x_abort(); + } + if (this->img_state[img->id] == 1) { + frame_skipping = img->draw (img, this->stream); + /* FIXME: Handle skipping */ + this->img_state[img->id] = 2; + } + } + + if (info->discard_fbuf && !info->discard_fbuf->id) { + printf ("libmpeg2:decode_data:BAD free_frame discard_fbuf=%p\n", info->discard_fbuf); + _x_abort(); + } + if (info->discard_fbuf && info->discard_fbuf->id) { + img = (vo_frame_t *) info->discard_fbuf->id; + if (this->img_state[img->id] != 2) { + printf ("libmpeg2:decode_data:free_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); + _x_abort(); + } + if (this->img_state[img->id] == 2) { + img->free(img); + this->img_state[img->id] = 0; + } + } +#endif + +} + +static void mpeg2_video_discontinuity (video_decoder_t *this_gen) { + mpeg2_video_decoder_t *this = (mpeg2_video_decoder_t *) this_gen; + +#ifdef LOG_ENTRY + printf ("libmpeg2: dicontinuity\n"); +#endif +/* mpeg2_discontinuity (&this->mpeg2dec); */ +} + +static void mpeg2_video_dispose (video_decoder_t *this_gen) { + + mpeg2_video_decoder_t *this = (mpeg2_video_decoder_t *) this_gen; + +#ifdef LOG_ENTRY + printf ("libmpeg2: close\n"); +#endif + + mpeg2_close (this->mpeg2dec); + + this->stream->video_out->close(this->stream->video_out, this->stream); + + free (this); +} + +static video_decoder_t *open_plugin (video_decoder_class_t *class_gen, xine_stream_t *stream) { + mpeg2_video_decoder_t *this ; + int32_t n; + + this = (mpeg2_video_decoder_t *) calloc(1, sizeof(mpeg2_video_decoder_t)); + + this->video_decoder.decode_data = mpeg2_video_decode_data; + this->video_decoder.flush = mpeg2_video_flush; + this->video_decoder.reset = mpeg2_video_reset; + this->video_decoder.discontinuity = mpeg2_video_discontinuity; + this->video_decoder.dispose = mpeg2_video_dispose; + this->stream = stream; + this->class = (mpeg2_class_t *) class_gen; + this->frame_number=0; + this->rff_pattern=0; + + this->mpeg2dec = mpeg2_init (); + mpeg2_custom_fbuf (this->mpeg2dec, 1); /* <- Force libmpeg2 to use xine frame buffers. */ + (stream->video_out->open) (stream->video_out, stream); + this->force_aspect = this->force_pan_scan = 0; + for(n=0;n<30;n++) this->img_state[n].id=0; + + return &this->video_decoder; +} + +/* + * mpeg2 plugin class + */ +static void *init_plugin (xine_t *xine, void *data) { + + mpeg2_class_t *this; + + this = (mpeg2_class_t *) calloc(1, sizeof(mpeg2_class_t)); + + this->decoder_class.open_plugin = open_plugin; + this->decoder_class.identifier = "mpeg2new"; + this->decoder_class.description = N_("mpeg2 based video decoder plugin"); + this->decoder_class.dispose = default_video_decoder_class_dispose; + + return this; +} +/* + * exported plugin catalog entry + */ + +static const uint32_t supported_types[] = { BUF_VIDEO_MPEG, 0 }; + +static const decoder_info_t dec_info_mpeg2 = { + supported_types, /* supported types */ + 6 /* priority */ +}; + +const plugin_info_t xine_plugin_info[] EXPORTED = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_VIDEO_DECODER, 19, "mpeg2new", XINE_VERSION_CODE, &dec_info_mpeg2, init_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; |