/* * mpeg2dec.c *^Copyright (C) 2000-2003 Michel Lespinasse *^Copyright (C) 1999-2000 Aaron Holtzman * * This file is part of mpeg2dec, a free MPEG-2 video stream decoder. * See http://libmpeg2.sourceforge.net/ for updates. * * 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 "config.h" #define USE_MPEG_DISPLAY 0 #include #include #include #include #include #include #include "SDL.h" #include #include "dsthallocal.h" #include "dstoslayer.h" #include "mpeg2.h" #include "mpeg2priv.h" #include "pd_dmx_priv.h" //#include "video_out.h" //#include "gettimeofday.h" #include #include #ifdef DMALLOC #include #endif static int buffer_size = 512000; static mpeg2dec_t * mpeg2dec; //static vo_open_t * output_open = NULL; //static vo_instance_t * output; static sdl_instance_t * sdl_handle; static int sigint = 0; static int total_offset = 0; static int verbose = 0; static int b_delete_fifo = 0; static block_fifo_t *fifo = (block_fifo_t *)NULL; static int b_stop_thread = 0; static mpeg_ud_callback_t *s_cbUserdata; void dump_state (FILE * f, mpeg2_state_t state, const mpeg2_info_t * info, int offset, int verbose); static void signal_handler (int sig) { sigint = 1; signal (sig, SIG_DFL); return; } static void print_fps (int final, int inc) { static uint32_t frame_counter = 0; static struct timeval tv_beg, tv_start; static int total_elapsed; static int last_count = 0; static int cnt = 0; struct timeval tv_end; double fps, tfps; int frames, elapsed; if (verbose) return; gettimeofday (&tv_end, NULL); if (!frame_counter) { tv_start = tv_beg = tv_end; signal (SIGINT, signal_handler); } elapsed = (tv_end.tv_sec - tv_beg.tv_sec) * 100 + (tv_end.tv_usec - tv_beg.tv_usec) / 10000; total_elapsed = (tv_end.tv_sec - tv_start.tv_sec) * 100 + (tv_end.tv_usec - tv_start.tv_usec) / 10000; if (final) { if (total_elapsed) tfps = frame_counter * 100.0 / total_elapsed; else tfps = 0; fprintf (stderr,"\n %s %d frames decoded in %.2f seconds (%.2f fps)\n", final ? "final" : "inprogress", frame_counter, total_elapsed / 100.0, tfps); return; } if ( inc ) frame_counter++; cnt++; if ( cnt == 4 ) cnt = 0; if (elapsed < 50) /* only display every 0.50 seconds */ return; tv_beg = tv_end; frames = frame_counter - last_count; fps = frames * 100.0 / elapsed; tfps = frame_counter * 100.0 / total_elapsed; fprintf (stderr, "in progress %c %d frames in %.2f sec (%.2f fps), " "%d last %.2f sec (%.2f fps)\033[K\r", cnt == 0 ? '-' : cnt == 1 ? '\\' : cnt == 2 ? '|' : cnt == 3 ? '/' : '.', frame_counter, total_elapsed / 100.0, tfps, frames, elapsed / 100.0, fps); last_count = frame_counter; } static void * malloc_hook (unsigned size, mpeg2_alloc_t reason) { void * buf; /* * Invalid streams can refer to fbufs that have not been * initialized yet. For example the stream could start with a * picture type onther than I. Or it could have a B picture before * it gets two reference frames. Or, some slices could be missing. * * Consequently, the output depends on the content 2 output * buffers have when the sequence begins. In release builds, this * does not matter (garbage in, garbage out), but in test code, we * always zero all our output buffers to: * - make our test produce deterministic outputs * - hint checkergcc that it is fine to read from all our output * buffers at any time */ if ((int)reason < 0) { return NULL; } buf = mpeg2_malloc (size, (mpeg2_alloc_t)-1); if (buf && (reason == MPEG2_ALLOC_YUV || reason == MPEG2_ALLOC_CONVERTED)) memset (buf, 0, size); return buf; } static void decode_mpeg2 (uint8_t * current, uint8_t * end) { const mpeg2_info_t * info; mpeg2_state_t state; static int n=0; //vo_setup_result_t setup_result; mpeg2_buffer (mpeg2dec, current, end); total_offset += end - current; info = mpeg2_info (mpeg2dec); while (1) { state = mpeg2_parse (mpeg2dec); #if 0 if (verbose) dump_state (stderr, state, info, total_offset - mpeg2_getpos (mpeg2dec), verbose); #endif switch (state) { case STATE_BUFFER: // sdl_discard (sdl_handle, info->discard_fbuf->buf, info->discard_fbuf->id); return; case STATE_SEQUENCE: /* might set nb fbuf, convert format, stride */ /* might set fbufs */ #if USE_MPEG_DISPLAY if (sdl_setup (sdl_handle, info->sequence->width, info->sequence->height, info->sequence->chroma_width, info->sequence->chroma_height)) { fprintf (stderr, "display setup failed\n"); exit (1); } #if 0 if (setup_result.convert && mpeg2_convert (mpeg2dec, setup_result.convert, NULL)) { fprintf (stderr, "color conversion setup failed\n"); exit (1); } #endif { uint8_t * buf[3]; void * id; sdl_setup_fbuf (sdl_handle, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); sdl_setup_fbuf (sdl_handle, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); sdl_setup_fbuf (sdl_handle, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); } #endif mpeg2_skip (mpeg2dec, 0/*(output->draw == NULL)*/); break; case STATE_PICTURE: /* send userdata here. */ if (s_cbUserdata && info->user_data_len && info->user_data) { unsigned long flag; flag = info->current_picture->flags & 0xFFFF; if ( info->current_picture->nb_fields > 1 ) flag |= (3<<16)/*FRAME*/; else if ( info->current_picture->flags & PIC_FLAG_TOP_FIELD_FIRST && info->current_picture->nb_fields == 1 ) flag |= (1<<16)/*TOP*/; else if ( info->current_picture->nb_fields == 1 ) flag |= (2<<16)/*BOTTOM*/; else flag |= (3<<16); //fprintf(stderr, "|%s:%d| s_cbUserdata: 0x%08lX, s_cbUserdata: 0x%08lX\n", __func__, __LINE__, s_cbUserdata, *s_cbUserdata); (*s_cbUserdata)((unsigned char *)info->user_data, info->user_data_len, (void *)flag); //fprintf(stderr, "|%s:%d|\n", __func__, __LINE__); } #if USE_MPEG_DISPLAY /* might skip */ /* might set fbuf */ sdl_start_fbuf (sdl_handle, info->current_fbuf->buf, info->current_fbuf->id); #endif break; case STATE_PICTURE_2ND: break; case STATE_SLICE: case STATE_END: case STATE_INVALID_END: /* draw current picture */ /* might free frame buffer */ #if USE_MPEG_DISPLAY if (info->display_fbuf #if 0 && (n++%3) == 0 #endif ) { sdl_draw_frame(sdl_handle, info->display_fbuf->buf, info->display_fbuf->id); } print_fps (0, 1); sdl_discard (sdl_handle, info->discard_fbuf->buf, info->discard_fbuf->id); #endif break; default: break; } } } static void es_loop (void) { uint8_t * buffer = (uint8_t *) malloc (buffer_size); uint8_t * end; block_t * p_bk, *p_cur_bk; int n; if ( buffer == NULL ) { fprintf(stderr, "!!! cannot allocate buffer (%d)\n", buffer_size); return; } do { if ( b_delete_fifo == 1 || !fifo ) { /* * If user ask to delete fifo, then we will delete here. */ if ( fifo ) { block_FifoRelease( fifo ); fifo = NULL; sdl_discard_all_buffer( sdl_handle ); mpeg2_reset( mpeg2dec, 1 ); } goto delay_and_continue; } print_fps(0, 0); p_bk = block_FifoGet( fifo ); if ( p_bk == (block_t *)NULL ) goto delay_and_continue; //block_ChainRelease( p_bk ); //continue; p_cur_bk = p_bk; if ( !p_bk->i_buffer ) { /* no data available */ block_ChainRelease( p_bk ); goto delay_and_continue; } while ( p_cur_bk ) { if ( !p_cur_bk->i_buffer ) break; if ( p_cur_bk->i_buffer > buffer_size ) fprintf(stderr, "!!! too big buffer is needed! %d\n", p_cur_bk->i_buffer); memcpy( buffer, p_cur_bk->p_buffer, p_cur_bk->i_buffer ); end = buffer + p_cur_bk->i_buffer; decode_mpeg2 ( buffer, end ); p_cur_bk = p_cur_bk->p_next; } block_ChainRelease( p_bk ); //continue; delay_and_continue: OS_mDelay(10); } while (!b_stop_thread && !sigint); fprintf(stderr, "\n\n\n\n\n\nmpeg decoder is ended!\n\n\n\n\n"); free (buffer); } static OS_TASK_ID s_MpegTaskId; void tMpegDec(DS_U32 arg) { es_loop (); print_fps (1, 1); } void mpeg_trigger_delete_fifo(int en) { b_delete_fifo = en; } void mpeg_set_fifo(block_fifo_t *p_fifo) { if ( fifo ) { do { OS_mDelay(1000); printf("Wait for fifo to be null.\n"); } while (fifo); } fifo = p_fifo; } int init_mpeg2dec(int width, int height) { static int b_init = 0; if ( b_init == 1 ) return 0; b_init = 1; sdl_handle = sdl_open (width, height); if (sdl_handle == NULL) { fprintf (stderr, "Can not open output\n"); return 1; } #if 1 mpeg2dec = mpeg2_init (); if (mpeg2dec == NULL) { fprintf(stderr, "!!! cannot initialize mpeg2dec\n"); return 1; } #endif s_MpegTaskId = OS_SpawnTask( tMpegDec, "tMpegDec", 0, 0, (DS_U32)0); if (s_MpegTaskId == (OS_TASK_ID)NULL) { printf("Cannot create tMpegDec task\n"); return 1; } mpeg2_malloc_hooks (malloc_hook, NULL); return 0; } int close_mpeg2dec() { b_stop_thread = 1; mpeg2_close (mpeg2dec); return 0; } void mpeg_set_userdata_callback(void *cbUserdata) { // printf("s_cbUserdata <- 0x%08lX\n", cbUserdata); s_cbUserdata = cbUserdata; }