/* * decode.c * Copyright (C) 1999-2000 Aaron Holtzman * * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. * * mpeg2dec 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. * * mpeg2dec 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 */ #include #include #include #include #include #include "config.h" #include "mpeg2.h" #include "mpeg2_internal.h" #include "motion_comp.h" #include "idct.h" #include "header.h" #include "slice.h" #include "stats.h" #ifdef ARCH_X86 #include "mmx.h" #endif //this is where we keep the state of the decoder picture_t picture; //global config struct mpeg2_config_t config; // the maximum chunk size is determined by vbv_buffer_size which is 224K for // MP@ML streams. (we make no pretenses ofdecoding anything more than that) static uint8_t chunk_buffer[224 * 1024 + 4]; static uint32_t shift = 0; static int is_display_initialized = 0; static int is_sequence_needed = 1; static int drop_flag = 0; static int drop_frame = 0; unsigned int i = 0; static uint8_t *tmp = NULL; void mpeg2_init (void) { config.flags = MPEG2_MMX_ENABLE; shift = 0; is_sequence_needed = 1; header_state_init (&picture); idct_init (); motion_comp_init (); } static void decode_allocate_image_buffers (picture_t * picture) { int frame_size; char *tmp = NULL; frame_size = picture->coded_picture_width * picture->coded_picture_height; tmp = (char *) malloc(picture->coded_picture_width* picture->coded_picture_height*12/8); picture->throwaway_frame[0] = tmp; picture->throwaway_frame[1] = tmp + frame_size * 5 / 4; picture->throwaway_frame[2] = tmp + frame_size; tmp = (char *) malloc(picture->coded_picture_width* picture->coded_picture_height*12/8); picture->backward_reference_frame[0] = tmp; picture->backward_reference_frame[1] = tmp + frame_size * 5 / 4; picture->backward_reference_frame[2] = tmp + frame_size; tmp = (char *) malloc(picture->coded_picture_width* picture->coded_picture_height*12/8); picture->forward_reference_frame[0] = tmp; picture->forward_reference_frame[1] = tmp + frame_size * 5 / 4; picture->forward_reference_frame[2] = tmp + frame_size; } static void decode_reorder_frames (void) { if (picture.picture_coding_type != B_TYPE) { //reuse the soon to be outdated forward reference frame picture.current_frame[0] = picture.forward_reference_frame[0]; picture.current_frame[1] = picture.forward_reference_frame[1]; picture.current_frame[2] = picture.forward_reference_frame[2]; //make the backward reference frame the new forward reference frame picture.forward_reference_frame[0] = picture.backward_reference_frame[0]; picture.forward_reference_frame[1] = picture.backward_reference_frame[1]; picture.forward_reference_frame[2] = picture.backward_reference_frame[2]; picture.backward_reference_frame[0] = picture.current_frame[0]; picture.backward_reference_frame[1] = picture.current_frame[1]; picture.backward_reference_frame[2] = picture.current_frame[2]; } else { picture.current_frame[0] = picture.throwaway_frame[0]; picture.current_frame[1] = picture.throwaway_frame[1]; picture.current_frame[2] = picture.throwaway_frame[2]; } } #ifdef __OMS__ static int parse_chunk (plugin_output_video_t *output, int code, uint8_t *buffer) #else static int parse_chunk (void * output, int code, uint8_t * buffer) #endif { int is_frame_done = 0; if (is_sequence_needed && code != 0xb3) /* b3 = sequence_header_code */ return 0; stats_header (code, buffer); switch (code) { case 0x00: /* picture_start_code */ if (header_process_picture_header (&picture, buffer)) { printf ("bad picture header\n"); } drop_frame = drop_flag && (picture.picture_coding_type == B_TYPE); decode_reorder_frames (); break; case 0xb3: /* sequence_header_code */ if (header_process_sequence_header (&picture, buffer)) { printf ("bad sequence header\n"); } is_sequence_needed = 0; if (!is_display_initialized) { decode_allocate_image_buffers (&picture); is_display_initialized = 1; } break; case 0xb5: /* extension_start_code */ if (header_process_extension (&picture, buffer)) { printf ("bad extension\n"); } break; default: if (code >= 0xb9) printf ("stream not demultiplexed ?\n"); if (code >= 0xb0) break; if (!drop_frame) { uint8_t ** bar; is_frame_done = slice_process (&picture, code, buffer); if (picture.picture_coding_type == B_TYPE) bar = picture.throwaway_frame; else bar = picture.forward_reference_frame; if ((HACK_MODE < 2) && (!picture.mpeg1)) { uint8_t * foo[3]; int offset; offset = (code-1) * 4 * picture.coded_picture_width; if ((! HACK_MODE) && (picture.picture_coding_type == B_TYPE)) offset = 0; foo[0] = bar[0] + 4 * offset; foo[1] = bar[1] + offset; foo[2] = bar[2] + offset; if(output != NULL) { /* * Y */ memcpy( ((char **) output)[0] + (code-1)*picture.coded_picture_width*16, foo[0], picture.coded_picture_width*16); /* * U, V */ memcpy( ((char **) output)[1] + (code-1)*picture.coded_picture_width*4, foo[2], picture.coded_picture_width*4); memcpy( ((char **) output)[2] + (code-1)*picture.coded_picture_width*4, foo[1], picture.coded_picture_width*4); } //output->draw_slice (foo, code-1); } else if (is_frame_done && output != NULL) { /* * Y */ memcpy( ((char **) output)[0], bar[0], picture.coded_picture_width*picture.coded_picture_height); /* * U, V */ memcpy( ((char **) output)[1], bar[2], picture.coded_picture_width*picture.coded_picture_height/4); memcpy( ((char **) output)[2], bar[1], picture.coded_picture_width*picture.coded_picture_height/4); } #ifdef ARCH_X86 if (config.flags & MPEG2_MMX_ENABLE) emms (); #endif } else { drop_flag = 0; drop_frame = 0; } } return is_frame_done; } int mpeg2_width(void) { return picture.coded_picture_width; } int mpeg2_height(void) { return picture.coded_picture_height; } int mpeg2_frame_rate_code(void) { XMPS_DEBUG("mpeg1 = %d", picture.mpeg1); return picture.frame_rate_code; } int mpeg2_decode_data (void *yuv_output, xmps_system_plugin_t *system, unsigned int stream_id) { static uint8_t code = 0xff; static uint8_t *chunk_ptr = chunk_buffer; uint8_t byte = 0; int ret = 0; if(tmp == NULL) { tmp = (uint8_t *) malloc(512); } while(ret == 0) { while (1) { if(i == 0) { system->read(system, stream_id, tmp, 512); } while(i < 512) { byte = tmp[i++]; //system->read(system, stream_id, &byte, 1); *chunk_ptr++ = byte; shift = (shift | byte) << 8; if (shift == 0x00000100) { if(i < 512) { byte = tmp[i++]; *chunk_ptr++ = byte; } else { system->read(system, stream_id, tmp, 512); i = 0; byte = tmp[i++]; *chunk_ptr++ = byte; } break; } } if (shift == 0x00000100) break; i = 0; } /* found start_code following chunk */ ret += parse_chunk (yuv_output, code, chunk_buffer); /* done with header or slice, prepare for next one */ code = byte; chunk_ptr = chunk_buffer; shift = 0xffffff00; } //XMPS_DEBUG("ret : %d", ret); return ret; } void mpeg2_closempeg2 (void) { } void mpeg2_drop (int flag) { drop_flag += flag; } void mpeg2_output_init (int flag) { is_display_initialized = flag; }