/* * 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 * * $Id: xine_decoder.c,v 1.20 2004/03/03 20:09:13 mroi Exp $ * * stuff needed to turn libmpeg2 into a xine decoder plugin */ #include #include #include #include #include #include #include #include #include "./include/mpeg2.h" #include "xine_internal.h" #include "video_out.h" #include "buffer.h" /* #define LOG #define LOG_FRAME_ALLOC_FREE #define LOG_ENTRY #define LOG_FRAME_COUNTER */ typedef struct { video_decoder_class_t decoder_class; } mpeg2_class_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; uint32_t img_state[30]; uint32_t frame_number; uint32_t rff_pattern; } mpeg2_video_decoder_t; static void mpeg2_video_print_bad_state(uint32_t * img_state) { int32_t n,m; m=0; for(n=0;n<30;n++) { if (img_state[n]>0) { printf("%d = %u\n",n, img_state[n]); m++; } } if (m > 3) _x_abort(); if (m == 0) printf("NO FRAMES\n"); } 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) info->sequence->pixel_width = this->force_aspect; 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(this->stream, XINE_META_INFO_VIDEOCODEC, "MPEG 2 (libmpeg2new)"); } else { _x_meta_info_set(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 %p (id=%d)\n", img,img->id); #endif if (this->img_state[img->id] != 0) { printf ("libmpeg2:decode_data:get_frame id=%d BAD STATE:%d\n", img->id, this->img_state[img->id]); _x_abort(); } this->img_state[img->id] = 1; 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 %p, id=%d \n", info->display_fbuf, img->id); #endif 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; #ifdef LOG_FRAME_ALLOC_FREE printf ("libmpeg2:decode_data:free_frame %p,id=%d\n", info->discard_fbuf, img->id); #endif 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; } } #ifdef LOG_FRAME_ALLOC_FREE mpeg2_video_print_bad_state(this->img_state); #endif break; case STATE_GOP: break; default: 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 #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 *) xine_xmalloc (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]=0; return &this->video_decoder; } /* * mpeg2 plugin class */ static char *get_identifier (video_decoder_class_t *this) { return "mpeg2new"; } static char *get_description (video_decoder_class_t *this) { return "mpeg2 based video decoder plugin"; } static void dispose_class (video_decoder_class_t *this) { free (this); } static void *init_plugin (xine_t *xine, void *data) { mpeg2_class_t *this; this = (mpeg2_class_t *) xine_xmalloc (sizeof (mpeg2_class_t)); this->decoder_class.open_plugin = open_plugin; this->decoder_class.get_identifier = get_identifier; this->decoder_class.get_description = get_description; this->decoder_class.dispose = dispose_class; return this; } /* * exported plugin catalog entry */ static uint32_t supported_types[] = { BUF_VIDEO_MPEG, 0 }; static decoder_info_t dec_info_mpeg2 = { supported_types, /* supported types */ 6 /* priority */ }; plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_VIDEO_DECODER, 18, "mpeg2new", XINE_VERSION_CODE, &dec_info_mpeg2, init_plugin }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };