/****************************************************************************** *_Copyright (c) 2009 Digital Stream Technology Inc. All Rights Reserved. * * Module: pd_dmx.c * * Description * Pseudo Driver for Demux * * @author Junku Park (hwatk@dstreamtech.com) * @version $Revision: 1.2 $ * ******************************************************************************/ #include "dsthalcfg.h" #include "dsthallocal.h" #include "dsthalerror.h" #include "dstoslayer.h" #include "dsthaldtv.h" #include "pd_dmx.h" #include "pd_dmx_priv.h" #include #include #include #include #include #include #include #include #include #ifdef DMALLOC #include #endif #include "mpeg2.h" /****************************************************************************** * Global variable declaration ******************************************************************************/ #define SAVE_ON_FILE 1 #undef USE_MPEG2DEC #define USE_MPEG2DEC 0 #ifndef USE_MPEG2DEC #error USE_MPEG2DEC is not defined! #endif /****************************************************************************** * Imported variable declaration ******************************************************************************/ /****************************************************************************** * Imported function declaration ******************************************************************************/ /****************************************************************************** * Local definitions ******************************************************************************/ #define LOCK_PD_DMX_MUTEX() do { \ if ( OS_TakeMutex(s_DrvDmxMutex) != 0 ) { \ printf("|%s| MUTEX ERROR, LINE=%d\n", __FUNCTION__, __LINE__); \ goto done; \ } \ } while (0) #define UNLOCK_PD_DMX_MUTEX() do { \ if ( OS_GiveMutex(s_DrvDmxMutex) != 0 ) { \ printf("|%s| MUTEX ERROR, LINE=%d\n", __FUNCTION__, __LINE__); \ } \ } while (0) #define DMX_LIST_INIT(pfirst) do { (pfirst) = NULL; } while(0) #define DMX_LIST_ADD(pfirst,pnew) do { \ if ( (pfirst) ) \ { \ (pnew)->pNext = (pfirst); \ (pfirst) = (pnew); \ } \ else \ { \ (pfirst) = (pnew); \ } \ } while (0) #define DMX_LIST_DEL(pfirst,pcur,pdel) do { \ if ( (pfirst) != (pdel) ) \ { \ (pcur) = (pfirst); \ while ( (pcur)->pNext != (pdel) ) \ (pcur) = (pcur)->pNext; \ (pcur)->pNext = (pdel)->pNext; \ } \ else \ { \ (pfirst) = (pdel)->pNext; \ } \ } while (0) /****************************************************************************** * Local typedefs ******************************************************************************/ typedef struct { DS_U8 i_objectTypeIndication; DS_U8 i_streamType; DS_BOOL b_upStream; DS_U32 i_bufferSizeDB; DS_U32 i_maxBitrate; DS_U32 i_avgBitrate; int i_decoder_specific_info_len; DS_U8 *p_decoder_specific_info; } decoder_config_descriptor_t; typedef struct { DS_BOOL b_useAccessUnitStartFlag; DS_BOOL b_useAccessUnitEndFlag; DS_BOOL b_useRandomAccessPointFlag; DS_BOOL b_useRandomAccessUnitsOnlyFlag; DS_BOOL b_usePaddingFlag; DS_BOOL b_useTimeStampsFlags; DS_BOOL b_useIdleFlag; DS_BOOL b_durationFlag; DS_U32 i_timeStampResolution; DS_U32 i_OCRResolution; DS_U8 i_timeStampLength; DS_U8 i_OCRLength; DS_U8 i_AU_Length; DS_U8 i_instantBitrateLength; DS_U8 i_degradationPriorityLength; DS_U8 i_AU_seqNumLength; DS_U8 i_packetSeqNumLength; DS_U32 i_timeScale; DS_U16 i_accessUnitDuration; DS_U16 i_compositionUnitDuration; DS_U64 i_startDecodingTimeStamp; DS_U64 i_startCompositionTimeStamp; } sl_config_descriptor_t; typedef struct { DS_BOOL b_ok; DS_U16 i_es_id; DS_BOOL b_streamDependenceFlag; DS_BOOL b_OCRStreamFlag; DS_U8 i_streamPriority; char *psz_url; DS_U16 i_dependOn_es_id; DS_U16 i_OCR_es_id; decoder_config_descriptor_t dec_descr; sl_config_descriptor_t sl_descr; } es_mpeg4_descriptor_t; typedef struct { DS_U8 i_iod_label, i_iod_label_scope; /* IOD */ DS_U16 i_od_id; char *psz_url; DS_U8 i_ODProfileLevelIndication; DS_U8 i_sceneProfileLevelIndication; DS_U8 i_audioProfileLevelIndication; DS_U8 i_visualProfileLevelIndication; DS_U8 i_graphicsProfileLevelIndication; es_mpeg4_descriptor_t es_descr[255]; } iod_descriptor_t; typedef struct { //es_format_t fmt; //es_out_id_t *id; int i_pes_size; int i_pes_gathered; block_t *p_pes; block_t **pp_last; block_fifo_t *p_fifo; es_mpeg4_descriptor_t *p_mpeg4desc; int b_gather; } ts_es_t; typedef struct { int i_pid; DS_BOOL b_seen; DS_BOOL b_valid; int i_cc; /* countinuity counter */ /* PSI owner (ie PMT -> PAT, ES -> PMT */ // ts_psi_t *p_owner; dvbpsi_handle h_dvbpsi; int i_owner_number; /* */ // ts_psi_t *psi; int p_psi; ts_es_t *es; /* Some private streams encapsulate several ES (eg. DVB subtitles)*/ ts_es_t **extra_es; int i_extra_es; DS_BOOL bOneShot; /* Stream Type */ DS_U8 b_stream_type; /* Section Filter Lists */ PD_SECFILTER *p_sec; } ts_pid_t; typedef struct tag_demux_sys_t { //vlc_mutex_t csa_lock; int b_force; /* TS packet size (188, 192, 204) */ int i_packet_size; /* how many TS packet we read at once */ int i_ts_read; /* how many bytes we read */ DS_U32 i_firstread; DS_S64 i_nread; DS_U32 i_packet_rate; DS_U32 i_packet_rate_short; /* All pid */ ts_pid_t pid[8192]; /* All PMT */ DS_BOOL b_user_pmt; int i_pmt; ts_pid_t **pmt; /* */ DS_BOOL b_es_id_pid; //csa_t *csa; int i_csa_pkt_size; DS_BOOL b_silent; DS_BOOL b_udp_out; int fd; /* udp socket */ DS_U8 *buffer; DS_BOOL b_dvb_control; int i_dvb_program; DS_S64 i_dvb_start; DS_S64 i_dvb_length; // vlc_list_t *p_programs_list; DS_S64 i_cur_pcr; DS_U64 i_cur_pcr_offset; DS_S64 i_prev_pcr; DS_U64 i_prev_pcr_offset; DS_S64 i_first_pcr; DS_U64 i_first_pcr_offset; /* Input TS File */ char psz_infile[256]; FILE *p_infile; DS_U64 i_read; DS_U64 i_filesize; /* TS dump */ char *psz_file; /* file to dump data in */ FILE *p_file; /* filehandle */ DS_U64 i_write; /* bytes written */ DS_BOOL b_file_out; /* dump mode enabled */ /* */ DS_BOOL b_meta; DS_BOOL bContPlay; } demux_sys_t; void change_file(int num, int bReadList, int bForce, int bCont); /****************************************************************************** * Local variables declaration ******************************************************************************/ static int s_PD_DMX_DelayEn = 1; static PD_DMX_PID_CALLBACK s_PidCbFunc = (PD_DMX_PID_CALLBACK)NULL; static PD_DMX_SEC_CALLBACK s_SecCbFunc = (PD_DMX_SEC_CALLBACK)NULL; static DS_U32 s_SecCbArg = (DS_U32)NULL; static demux_sys_t s_DemuxSys; static demux_sys_t *p_DmxSys = &s_DemuxSys; static OS_MUTEX_ID s_DrvDmxMutex = (OS_MUTEX_ID)NULL; static OS_TASK_ID s_dmxThreadId = (OS_TASK_ID)NULL; static DS_BOOL s_bDrvDmxThreadRun = DS_FALSE; static DS_BOOL s_bDrvDmxThreadIdle = DS_FALSE; static DS_BOOL s_SetFile = DS_FALSE; static char *s_VideoOutputFile = (char *)NULL; static char *s_AudioOutputFile = (char *)NULL; static FILE *s_VideoOut = (FILE *)NULL; static FILE *s_AudioOut = (FILE *)NULL; FILE *s_StreamVidOut, *s_StreamAudOut; static DS_BOOL b_pdemux_start = DS_FALSE; int pd_dmx_dbg = 1; /****************************************************************************** * Local function prototypes ******************************************************************************/ static int Init( demux_sys_t *p_sys ); static int Open( demux_sys_t *p_sys ); static int Close( demux_sys_t *p_sys ); static int Control( demux_sys_t *p_sys, int i_query, va_list args ); static int Demux( demux_sys_t *p_sys ); static int PD_DMX_InitDemuxTask(void); static int PD_DMX_StartDemuxTask(void); static int PD_DMX_StopDemuxTask(void); int PD_DMX_SetOutputFile(const char *video_output, const char *audio_output); static inline int stream_Seek( demux_sys_t *p_sys, DS_S64 i_pos ); #if 0 ___COMMON_APIs___() #endif int PD_DMX_OpenDemux(void) { int retVal = PD_DMX_SUCCESS; //if ( retVal ) // goto done; //SysASSERT( PD_DMX_OK(retVal) ); retVal = Init(&s_DemuxSys); SysASSERT( PD_DMX_OK(retVal) ); retVal = PD_DMX_InitDemuxTask(); SysASSERT( PD_DMX_OK(retVal) ); s_DrvDmxMutex = OS_CreateMutex( "DrvDmxMutex" ); if ( s_DrvDmxMutex == (OS_MUTEX_ID)NULL ) { printf("|%s:%d| ERROR\n", __FUNCTION__, __LINE__); retVal = PD_DMX_EOUTOFMEMORY; goto done; } b_pdemux_start = DS_FALSE; change_file(0, 0, 0, 1); //PD_DMX_SetOutputFile("./video.vid", "./audio.ac3"); done: return retVal; } int PD_DMX_SetDemuxFile(const char *Pathname, DS_BOOL bContiPlay) { int retVal = PD_DMX_SUCCESS; FILE *p_fp; if (Pathname) memcpy( p_DmxSys->psz_infile, Pathname, 256 ); else { retVal = PD_DMX_EGENERIC; goto notValidFile; } if ( s_DrvDmxMutex ) { LOCK_PD_DMX_MUTEX(); } p_fp = fopen( p_DmxSys->psz_infile, "rb" ); if ( p_fp == (FILE *)NULL ) { printf("|%s:%d| ERROR: Cannot open file, %s.\n", __FUNCTION__, __LINE__, p_DmxSys->psz_infile ); retVal = PD_DMX_EGENERIC; if ( s_DrvDmxMutex ) { UNLOCK_PD_DMX_MUTEX(); } goto notValidFile; } if ( p_DmxSys->p_infile != (FILE *)NULL ) { fclose( p_DmxSys->p_infile ); } p_DmxSys->p_infile = p_fp; p_DmxSys->bContPlay = bContiPlay; s_SetFile = DS_TRUE; if ( s_DrvDmxMutex ) { UNLOCK_PD_DMX_MUTEX(); } done: return retVal; notValidFile: p_DmxSys->p_infile = NULL; s_SetFile = DS_FALSE; return retVal; } int PD_DMX_StartDemux(void) { int retVal = PD_DMX_SUCCESS; if ( s_SetFile == DS_FALSE ) { printf("!!! No file is selected yet, just skip this.\n"); return retVal; } if (b_pdemux_start == DS_TRUE ) return retVal; retVal = Open( &s_DemuxSys ); SysASSERT( PD_DMX_OK(retVal) ); retVal = PD_DMX_StartDemuxTask(); SysASSERT( PD_DMX_OK(retVal) ); b_pdemux_start = DS_TRUE; return retVal; } int PD_DMX_StopDemux(void) { int retVal = PD_DMX_SUCCESS; if (b_pdemux_start == DS_FALSE ) return retVal; retVal = PD_DMX_StopDemuxTask(); SysASSERT( PD_DMX_OK(retVal) ); b_pdemux_start = DS_FALSE; return retVal; } int PD_DMX_CloseDemux(void) { int retVal = PD_DMX_SUCCESS; retVal = Close( &s_DemuxSys ); if ( s_VideoOut ) { fclose(s_VideoOut); s_AudioOut = (FILE *)NULL; } if ( s_AudioOut ) { fclose(s_AudioOut); s_AudioOut = (FILE *)NULL; } return retVal; } DS_BOOL PD_DMX_IsDemuxStarted(void) { return b_pdemux_start; } int PD_DMX_SetOutputFile(const char *video_output, const char *audio_output) { int retVal = PD_DMX_SUCCESS; int n; DS_BOOL b_mutex_lock = DS_FALSE; if ( !video_output || !audio_output ) return PD_DMX_EGENERIC; LOCK_PD_DMX_MUTEX(); b_mutex_lock = DS_TRUE; if ( s_VideoOutputFile == (char *)NULL ) s_VideoOutputFile = malloc(256); if ( s_AudioOutputFile == (char *)NULL ) s_AudioOutputFile = malloc(256); SysASSERT( s_VideoOutputFile && s_AudioOutputFile ); #if SAVE_ON_FILE if ( s_VideoOutputFile == (char *)NULL ) { retVal = PD_DMX_EGENERIC; goto done; } if ( s_AudioOutputFile == (char *)NULL ) { retVal = PD_DMX_EGENERIC; goto done; } #endif n = strlen(video_output); memcpy( s_VideoOutputFile, video_output, n >= 256 ? 256 : n ); n = strlen(audio_output); memcpy( s_AudioOutputFile, audio_output, n >= 256 ? 256 : n ); if ( s_VideoOut ) { fclose(s_VideoOut); s_VideoOut = (FILE *)NULL; } if ( s_AudioOut ) { fclose(s_AudioOut); s_AudioOut = (FILE *)NULL; } #if SAVE_ON_FILE s_VideoOut = fopen( s_VideoOutputFile, "wb" ); //s_VideoOut = popen( "./mpeg2dec -o sdl", "w" ); if ( s_VideoOut == (FILE *)NULL ) { perror("Cannot open/create the file."); printf("Cannot open/create the file %s\n", s_VideoOutputFile); retVal = PD_DMX_EGENERIC; goto done; } s_AudioOut = fopen( s_AudioOutputFile, "wb" ); if ( s_AudioOut == (FILE *)NULL ) { printf("Cannot open/create the file %s\n", s_AudioOutputFile); retVal = PD_DMX_EGENERIC; goto done; } #else if ( s_StreamVidOut ) { fclose(s_StreamVidOut); s_StreamVidOut = (FILE *)NULL; } if ( s_StreamAudOut ) { fclose(s_StreamAudOut); s_StreamAudOut = (FILE *)NULL; } // // Open video pipe. // if ( pipe(vidFd) ) { printf("!!! cannot open video pipe.\n"); retVal = PD_DMX_EGENERIC; goto done; } s_VideoOut = fdopen( vidFd[1], "w" ); s_StreamVidOut = fdopen( vidFd[0], "r" ); //s_StreamVidOut = fopen( "./video.vid", "rb" ); if ( !s_VideoOut || !s_StreamVidOut ) { fprintf(stderr, "!!! cannot open video pipe, 0x%lx, 0x%lx\n", (DS_U32)s_VideoOut, (DS_U32)s_StreamVidOut); retVal = PD_DMX_EGENERIC; goto done; } // // Open audio pipe. // if ( pipe(audFd) ) { printf("!!! cannot open audio pipe.\n"); retVal = PD_DMX_EGENERIC; goto done; } s_AudioOut = fdopen( audFd[1], "wb" ); s_StreamAudOut = fdopen( audFd[0], "rb" ); if ( !s_AudioOut || !s_StreamAudOut ) { fprintf(stderr, "!!! cannot open audio pipe, 0x%lx, 0x%lx\n", (DS_U32)s_AudioOut, (DS_U32)s_StreamAudOut); retVal = PD_DMX_EGENERIC; goto done; } #endif done: if ( b_mutex_lock ) UNLOCK_PD_DMX_MUTEX(); return retVal; } int PD_DMX_EnableDelay(int En) { s_PD_DMX_DelayEn = En; return 0; } #if 0 ___PID_Section_Filter_APIs__() #endif int PD_DMX_SetPidFilterCallback(PD_DMX_PID_CALLBACK CbFunc) { int retVal = PD_DMX_SUCCESS; s_PidCbFunc = CbFunc; return retVal; } int PD_DMX_SetSectionFilterCallback(PD_DMX_SEC_CALLBACK CbFunc, DS_U32 Argument) { int retVal = PD_DMX_SUCCESS; s_SecCbFunc = CbFunc; s_SecCbArg = Argument; return retVal; } int PD_DMX_CreatePIDFilter(int Pid, DS_U8 streamType, DS_BOOL bOneShot) { int retVal = PD_DMX_SUCCESS; ts_pid_t* pPid = (ts_pid_t *)NULL; SysREQUIRE( (Pid < 8192) && (Pid >= 0) ); LOCK_PD_DMX_MUTEX(); pPid = &p_DmxSys->pid[Pid]; pPid->b_valid = DS_TRUE; pPid->bOneShot = bOneShot; pPid->i_owner_number++; pPid->b_stream_type = streamType; pPid->es = (ts_es_t *)malloc(sizeof(ts_es_t)); memset( pPid->es, 0, sizeof(ts_es_t)); pPid->es->p_pes = NULL; pPid->es->pp_last = &pPid->es->p_pes; pPid->es->p_fifo = block_FifoNew(); if (pPid->es->p_fifo == (block_fifo_t *)NULL ) { printf("!!! cannot allocate block_fifo.\n"); retVal = PD_DMX_EGENERIC; } #if USE_MPEG2DEC if ( streamType == MPEG2_VIDEO_STREAM ) mpeg_set_fifo( pPid->es->p_fifo ); mpeg_trigger_delete_fifo(0); #endif UNLOCK_PD_DMX_MUTEX(); done: return retVal; } int PD_DMX_DeletePIDFilter(int Pid) { int retVal = PD_DMX_SUCCESS; ts_pid_t* pPid = (ts_pid_t *)NULL; fprintf(stderr, "Pid: %d\n", Pid); SysREQUIRE( (Pid < 8192) && (Pid >= 0) ); LOCK_PD_DMX_MUTEX(); pPid = &p_DmxSys->pid[Pid]; pPid->b_valid = DS_FALSE; pPid->i_owner_number = 0; if (pPid->es) { if (pPid->es->p_fifo) { block_FifoEmpty(pPid->es->p_fifo); block_FifoWake(pPid->es->p_fifo); if ( pPid->b_stream_type == MPEG2_VIDEO_STREAM ) { #if USE_MPEG2DEC mpeg_trigger_delete_fifo(1); #endif } else { block_FifoRelease(pPid->es->p_fifo); } pPid->es->p_fifo = (block_fifo_t *)NULL; } free(pPid->es); } pPid->b_stream_type = 0; UNLOCK_PD_DMX_MUTEX(); done: return retVal; } PD_SECFILTER *PD_DMX_CreateSectionFilter(int Pid, DS_U8* FilterBytes, DS_U8* FilterMasks, int FilterSiz, int SectionSize, int bOneShot) { PD_SECFILTER* p_newsec = (PD_SECFILTER *)NULL; ts_pid_t* pPid; SysREQUIRE( (Pid < 8192) && (Pid >= 0) ); SysREQUIRE( FilterSiz <= MAX_FILTER_SIZE ); LOCK_PD_DMX_MUTEX(); pPid = &p_DmxSys->pid[Pid]; pPid->b_valid = DS_TRUE; pPid->i_owner_number++; p_newsec = (PD_SECFILTER *)malloc( sizeof(PD_SECFILTER) ); SysASSERT( p_newsec ); p_newsec->i_pid = Pid; p_newsec->i_section_size = SectionSize; p_newsec->pNext = (PD_SECFILTER *)NULL; memcpy( p_newsec->FilterBytes, FilterBytes, FilterSiz ); memcpy( p_newsec->FilterMask, FilterMasks, FilterSiz ); p_newsec->i_filter_size = FilterSiz; p_newsec->bEnable = DS_TRUE; #if 0 if ( pPid->p_sec == (PD_SECFILTER *)NULL ) { /* This is first time to add section filter to specified PID filter. */ } else { /* Section filter exists already. Have to add end of section filter. */ } #else DMX_LIST_ADD( pPid->p_sec, p_newsec ); #endif UNLOCK_PD_DMX_MUTEX(); done: return p_newsec; } int PD_DMX_EnableSectionFilter( PD_SECFILTER* p_sec, DS_BOOL bEnable) { int retVal = PD_DMX_SUCCESS; SysASSERT( p_sec ); LOCK_PD_DMX_MUTEX(); p_sec->bEnable = bEnable; UNLOCK_PD_DMX_MUTEX(); done: return retVal; } int PD_DMX_DeleteSectionFilter( PD_SECFILTER* p_sec ) { int retVal = PD_DMX_SUCCESS; int Pid; ts_pid_t* pPid; PD_SECFILTER* p_firstsec = (PD_SECFILTER *)NULL; PD_SECFILTER* p_cursec = (PD_SECFILTER *)NULL; SysASSERT( p_sec ); LOCK_PD_DMX_MUTEX(); Pid = p_sec->i_pid; pPid = &p_DmxSys->pid[Pid]; p_firstsec = pPid->p_sec; pPid->i_owner_number--; DMX_LIST_DEL( p_firstsec, p_cursec, p_sec ); if ( pPid->i_owner_number == 0 ) { pPid->b_valid = DS_FALSE; pPid->p_sec = NULL; } UNLOCK_PD_DMX_MUTEX(); done: return retVal; } #if 0 ___MonitorThread___() #endif static void PD_DMX_MonitorDemuxThread(DS_U32 Arg) { #define TICKABS(x) ((x)>=0 ? (x) : (-(x))) demux_sys_t *p_sys = (demux_sys_t *)Arg; volatile DS_U32 curtick, stick; DS_U32 expectedTime, elapsedTime; while(1) { if (s_bDrvDmxThreadRun == DS_FALSE || p_sys->p_infile == NULL ) { s_bDrvDmxThreadIdle = DS_TRUE; OS_mDelay(100); continue; } s_bDrvDmxThreadIdle = DS_FALSE; stick = OS_GetTickCount(); if ( Demux(p_sys) == -1 /*EOF*/ && p_sys->bContPlay ) stream_Seek( p_sys, 0 ); curtick = OS_GetTickCount(); //if ( TICKABS(curtick-stick) && (TICKABS(curtick-stick) < 4) ) // OS_Delay(TICKABS(curtick-stick)); if ( p_sys->i_packet_rate && s_PD_DMX_DelayEn ) { expectedTime = TICKABS(p_sys->i_firstread + (p_sys->i_nread *100) / p_sys->i_packet_rate); elapsedTime = OS_GetTickCount(); if ( elapsedTime < expectedTime ) { if ( pd_dmx_dbg >= 2 ) { printf("i_firstread: %ld, nread: %lld, rate: %ld\n", p_sys->i_firstread, p_sys->i_nread, p_sys->i_packet_rate); printf("Delay %ld more. (Elapsed: %ld, Expected: %ld)\n", ( expectedTime-elapsedTime ), elapsedTime, expectedTime); } if ( ( expectedTime-elapsedTime ) > 100 ) OS_Delay( 100 ); else OS_Delay( expectedTime-elapsedTime ); } else { // printf("i_firstread: %ld, nread: %lld, rate: %ld\n", p_sys->i_firstread, p_sys->i_nread, p_sys->i_packet_rate); // printf("expectedTime: %ld, Elapsed: %ld\n", expectedTime, elapsedTime); } } } #undef TICKABS } static int PD_DMX_InitDemuxTask(void) { int retVal = 0; s_dmxThreadId = OS_SpawnTask( PD_DMX_MonitorDemuxThread, "tDrvDemuxThread", 0, 4096, (DS_U32)p_DmxSys); if ( s_dmxThreadId == (OS_TASK_ID)NULL ) { printf("ERROR: Cannot create function tDrvDemuxThread.\n"); retVal = PD_DMX_EOUTOFMEMORY; } return retVal; } static int PD_DMX_StartDemuxTask(void) { int retVal = 0; int n_retry = 0; s_bDrvDmxThreadRun = DS_TRUE; do { OS_mDelay(10); if ( s_bDrvDmxThreadIdle == DS_FALSE ) break; } while ( n_retry++ < 10 ); if ( n_retry >= 10 ) retVal = PD_DMX_ETIMEOUT; return retVal; } static int PD_DMX_StopDemuxTask(void) { int retVal = 0; int n_retry = 0; s_bDrvDmxThreadRun = DS_FALSE; do { OS_mDelay(10); if ( s_bDrvDmxThreadIdle == DS_TRUE ) break; } while ( n_retry++ < 10 ); return retVal; } #if 0 ___stream_Control_APIs___() #endif /****************************************************************************** * stream_Peek * @param p_sys is global demux system structure. * @param p_peek is next iSizRead bytes. * @param iSizRead to read * @return N total remain bytes on success, otherwise 0. ******************************************************************************/ static int stream_Peek( demux_sys_t *p_sys, const DS_U8 **p_peek, int iSizRead ) { static DS_U8 *pBuf = (DS_U8 *)NULL; long cur_pos = 0; int nRead = 0; if ( p_sys->p_infile == (FILE *)NULL ) { p_sys->p_infile = fopen( p_sys->psz_infile, "r" ); if ( p_sys->p_infile == (FILE *)NULL ) { printf("|%s:%d| ERROR: Cannot open file, %s\n", __FUNCTION__, __LINE__, p_sys->psz_infile ); goto done; } } if ( pBuf ) free( pBuf ); pBuf = (DS_U8 *)malloc( iSizRead ); SysASSERT( pBuf != (DS_U8 *)NULL ); cur_pos = ftell( p_sys->p_infile ); nRead = fread( pBuf, 1, iSizRead, p_sys->p_infile ); if ( nRead > 0 ) *p_peek = pBuf; else *p_peek = (DS_U8 *)NULL; SysCHECK( fseek( p_sys->p_infile, cur_pos, SEEK_SET ) == 0 ); done: return nRead; } /****************************************************************************** * stream_Control * @param p_sys is global demux system structure. * @param i_query to control the stream * @return 0 on success, otherwise non-zero value. ******************************************************************************/ static int stream_Control( demux_sys_t *p_sys, int i_query, ... ) { va_list args; int retVal = 0; DS_S64 *pi_pos; long cur_pos, end_pos; if ( p_sys == NULL ) return retVal; va_start( args, i_query ); /* * Do stream Control here. */ switch( i_query ) { case STREAM_GET_SIZE: pi_pos = (DS_S64 *)va_arg( args, DS_S64 * ); cur_pos = ftell( p_sys->p_infile ); SysCHECK( fseek( p_sys->p_infile, 0, SEEK_END ) == 0 ); end_pos = ftell( p_sys->p_infile ); *pi_pos = (DS_S64)end_pos; SysCHECK( fseek( p_sys->p_infile, cur_pos, SEEK_SET ) == 0 ); break; case STREAM_SET_POSITION: cur_pos = (DS_S64)va_arg( args, DS_S64 ); SysCHECK( fseek( p_sys->p_infile, cur_pos, SEEK_SET ) == 0 ); break; default: break; } va_end( args ); return retVal; } static inline DS_S64 stream_Size( demux_sys_t *p_sys ) { DS_S64 i_pos; stream_Control( p_sys, STREAM_GET_SIZE, &i_pos ); return i_pos; } static inline int stream_Seek( demux_sys_t *p_sys, DS_S64 i_pos ) { // printf("|%s:%d| Offset to 0, current time: %ld.\n", __FUNCTION__, __LINE__, OS_GetTickCount()); p_sys->i_nread = 0; return stream_Control( p_sys, STREAM_SET_POSITION, i_pos ); } static block_t *stream_Block( demux_sys_t *p_sys, int i_siz ) { block_t *p_bk = (block_t *)NULL; DS_U8 *pBuffRead = (DS_U8 *)0; int nRead = 0; long curpos; static long lastpos = 0; SysASSERT( p_sys->p_infile ); p_bk = block_Alloc( i_siz ); SysASSERT(p_bk); //printf("p_bk: 0x%08lX\n", p_bk); if ( p_bk == (block_t *)NULL ) { return p_bk; } LOCK_PD_DMX_MUTEX(); nRead = fread( p_bk->p_buffer, 1, i_siz, p_sys->p_infile ); if ( nRead > 0 ) { pBuffRead = &p_bk->p_buffer[0]; curpos = ftell( p_sys->p_infile ); if ( (curpos-lastpos) != p_sys->i_packet_size ) printf("!!! Number of last read is %d\n", (int)(curpos-lastpos)); lastpos = curpos; if ( p_sys->i_nread == 0 ) p_sys->i_firstread = OS_GetTickCount(); p_sys->i_nread += i_siz; if ( nRead != i_siz ) printf("nRead: %d, expected: %d\n", nRead, i_siz ); } else if ( nRead == 0 ) { lastpos = 0; block_Release( p_bk ); p_bk = (block_t *)NULL; } else { } UNLOCK_PD_DMX_MUTEX(); done: return p_bk; } /** * Try to read "i_read" bytes into a buffer pointed by "p_read". If * "p_read" is NULL then data are skipped instead of read. The return * value is the real numbers of bytes read/skip. If this value is less * than i_read that means that it's the end of the stream. */ static int stream_Read( demux_sys_t *p_sys, void *p_read, int i_read ) { int nRead = 0; long curpos; if ( p_read ) { nRead = fread( p_sys->buffer, 1, i_read, p_sys->p_infile ); if ( nRead > 0 ) { memcpy( p_read, p_sys->buffer, nRead < i_read ? nRead : i_read ); if ( p_sys->i_nread == 0 ) p_sys->i_firstread = OS_GetTickCount(); p_sys->i_nread += i_read; } } else { curpos = ftell( p_sys->p_infile ); fseek( p_sys->p_infile, curpos + i_read, SEEK_SET ); } return nRead; } static DS_S64 stream_Tell( demux_sys_t *p_sys ) { DS_S64 nPos = 0; nPos = (DS_S64)ftell( p_sys->p_infile ); return nPos; } #if 0 ___ES_Output___() #endif void _cbPESFilterCallback( ts_pid_t *p_pid, DS_U8 b_stream_type ) { block_t * p_bk, *p_cur_bk; PESInfo_t pesInfo; if ( b_stream_type != SUBTITLE_STREAM ) { //block_ChainRelease( p_block ); return; } p_bk = block_FifoGet( p_pid->es->p_fifo ); if ( p_bk == (block_t *)NULL ) return; if ( !p_bk->i_buffer ) { /* no data available */ block_ChainRelease( p_bk ); return; } p_cur_bk = p_bk; while ( p_cur_bk ) { if ( !p_cur_bk->i_buffer ) break; if ( p_cur_bk->i_buffer > 32*1024 ) fprintf(stderr, "!!! too big buffer is needed! %d\n", (int)p_cur_bk->i_buffer); if (s_PidCbFunc) { pesInfo.i_flags = p_cur_bk->i_flags; pesInfo.i_pts = p_cur_bk->i_pts; pesInfo.i_dts = p_cur_bk->i_dts; s_PidCbFunc( p_pid->i_pid, p_cur_bk->p_buffer, p_cur_bk->i_buffer, &pesInfo ); } p_cur_bk = p_cur_bk->p_next; } block_ChainRelease( p_bk ); } static void es_out_Send( ts_pid_t *p_pid, DS_U8 b_stream_type, block_t *p_block ) { block_t *p_cur_block = p_block; int nWrite; //fprintf(stderr, "p_block: 0x%08lX\n", p_block ); if ( b_stream_type == SUBTITLE_STREAM ) { block_FifoPut( p_pid->es->p_fifo, p_block ); _cbPESFilterCallback( p_pid, b_stream_type ); return; } /* * ÇöÀç´Â File Ãâ·Â¸¸ À¯Áö. */ if ( s_VideoOut && b_stream_type == MPEG2_VIDEO_STREAM ) { while ( p_cur_block ) { if ( p_cur_block->i_flags && BLOCK_FLAG_CORRUPTED ) { fprintf(stderr, "Corrupted packet.\n"); } else { nWrite = fwrite( p_cur_block->p_buffer, p_block->i_buffer, 1, s_VideoOut ); if ( nWrite != 1 ) { printf("ERROR: cannot write data into %s.\n", s_VideoOutputFile); return; } } p_cur_block = p_block->p_next; } } else if ( s_AudioOut && b_stream_type == AC3_AUDIO_STREAM ) { while ( p_cur_block ) { if ( p_cur_block->i_flags && BLOCK_FLAG_CORRUPTED ) { fprintf(stderr, "Corrupted packet.\n"); } else { nWrite = fwrite( p_cur_block->p_buffer, p_block->i_buffer, 1, s_AudioOut ); if ( nWrite != 1 ) { printf("ERROR: cannot write data into %s\n", s_AudioOutputFile); return; } } p_cur_block = p_block->p_next; } } #if USE_MPEG2DEC if ( p_pid->es->p_fifo ) #else if (0) #endif { block_FifoPut( p_pid->es->p_fifo, p_block ); } else { //printf( "!!! no fifo is available.\n"); block_ChainRelease( p_block ); } } #if 0 ___PID_Section_Filter___() #endif static inline int PIDGet( DS_U8 *p ) { return ( (p[1]&0x1f)<<8 )|p[2]; } static void PCRHandle( demux_sys_t *p_sys, ts_pid_t *pid, DS_U8 *p ) { DS_U32 rate_in_bits; DS_U64 bytes; DS_U32 elapsedTime; if( ( p[3]&0x20 ) && /* adaptation */ ( p[5]&0x10 ) && ( p[4] >= 7 ) ) { //int i; DS_S64 i_pcr; /* 33 bits (90 kHz based) */ // p[6] ->program_clock_reference_base[32:25] // p[7] ->program_clock_reference_base[24:17] // p[8] ->program_clock_reference_base[16:9] // p[9] ->program_clock_reference_base[8:1] // p[10] ->program_clock_reference_base[0] + reserved[5:0] + program_clock_reference_extension[8] // p[11] ->program_clock_reference_extension[7:0] i_pcr = ( (DS_S64)p[6] << 25 ) | ( (DS_S64)p[7] << 17 ) | ( (DS_S64)p[8] << 9 ) | ( (DS_S64)p[9] << 1 ) | ( (DS_S64)p[10] >> 7 ); // printf("i_pcr: %lld (@ %ld)\n", i_pcr, p_sys->i_nread); #if 0 /* Search program and set the PCR */ for( i = 0; i < p_sys->i_pmt; i++ ) { int i_prg; for( i_prg = 0; i_prg < p_sys->pmt[i]->psi->i_prg; i_prg++ ) { if( pid->i_pid == p_sys->pmt[i]->psi->prg[i_prg]->i_pid_pcr ) { es_out_Control( p_demux->out, ES_OUT_SET_GROUP_PCR, (int)p_sys->pmt[i]->psi->prg[i_prg]->i_number, (int64_t)(i_pcr * 100 / 9) ); } } } #else if ( p_sys->i_first_pcr_offset == 0 ) { p_sys->i_first_pcr_offset = p_sys->i_nread; p_sys->i_first_pcr = i_pcr; } p_sys->i_prev_pcr_offset = p_sys->i_cur_pcr_offset; p_sys->i_prev_pcr = p_sys->i_cur_pcr; p_sys->i_cur_pcr = i_pcr; p_sys->i_cur_pcr_offset = p_sys->i_nread; if ( p_sys->i_first_pcr_offset && p_sys->i_cur_pcr != p_sys->i_first_pcr ) { bytes = p_sys->i_cur_pcr_offset - p_sys->i_first_pcr_offset; elapsedTime = (p_sys->i_cur_pcr - p_sys->i_first_pcr); if ( elapsedTime ) rate_in_bits = ((bytes * 90000) / elapsedTime)*8; if ( rate_in_bits >= STREAM_BITRATE_MIN && rate_in_bits <= STREAM_BITRATE_MAX ) p_sys->i_packet_rate = rate_in_bits / 8; bytes = p_sys->i_cur_pcr_offset - p_sys->i_prev_pcr_offset; elapsedTime = (p_sys->i_cur_pcr - p_sys->i_prev_pcr); if ( elapsedTime ) rate_in_bits = ((bytes * 90000) / elapsedTime)*8; if ( rate_in_bits >= STREAM_BITRATE_MIN && rate_in_bits <= STREAM_BITRATE_MAX ) p_sys->i_packet_rate_short = rate_in_bits / 8; if ( pd_dmx_dbg > 3 ) printf("Stream bitrate: %ld\n", rate_in_bits ); } if ( pd_dmx_dbg > 4 ) printf("PCR: %lld\n", i_pcr * 100 / 9 ); #endif } } void dump(DS_U8 *pBuf) { int i, j; for (i=0; i<16; i++) { for (j=0; j<16; j++) printf("%02X ", pBuf[i*16+j]); printf("\n"); } } /**************************************************************************** * gathering stuff ****************************************************************************/ static void ParsePES( demux_sys_t *p_demux, ts_pid_t *pid ) { block_t *p_pes = pid->es->p_pes; uint8_t header[34]; int i_pes_size = 0; int i_skip = 0; DS_S64 i_dts = -1; DS_S64 i_pts = -1; DS_S64 i_length = 0; int i_max; // int i; /* remove the pes from pid */ pid->es->p_pes = NULL; pid->es->i_pes_size= 0; pid->es->i_pes_gathered= 0; pid->es->pp_last = &pid->es->p_pes; /* FIXME find real max size */ memset( header, 0, sizeof(uint8_t)*34 ); i_max = block_ChainExtract( p_pes, header, 34 ); if( header[0] != 0 || header[1] != 0 || header[2] != 1 ) { //if (pd_dmx_dbg) printf("invalid header [0x%x:%x:%x:%x] (pid: %d)", header[0], header[1],header[2],header[3], pid->i_pid ); // // TBD: 000001xx¸¦ ãÀ»¶§±îÁö ã¾Æ¾ß ÇÑ´Ù. // block_ChainRelease( p_pes ); return; } /* TODO check size */ switch( header[3] ) { case 0xBC: /* Program stream map */ case 0xBE: /* Padding */ case 0xBF: /* Private stream 2 */ case 0xF0: /* ECM */ case 0xF1: /* EMM */ case 0xFF: /* Program stream directory */ case 0xF2: /* DSMCC stream */ case 0xF8: /* ITU-T H.222.1 type E stream */ i_skip = 6; break; default: if( ( header[6]&0xC0 ) == 0x80 ) { /* mpeg2 PES */ i_skip = header[8] + 9; if( header[7]&0x80 ) /* has pts */ { i_pts = ((DS_S64)(header[ 9]&0x0e ) << 29)| (DS_S64)(header[10] << 22)| ((DS_S64)(header[11]&0xfe) << 14)| (DS_S64)(header[12] << 7)| (DS_S64)(header[13] >> 1); if( header[7]&0x40 ) /* has dts */ { i_dts = ((DS_S64)(header[14]&0x0e ) << 29)| (DS_S64)(header[15] << 22)| ((DS_S64)(header[16]&0xfe) << 14)| (DS_S64)(header[17] << 7)| (DS_S64)(header[18] >> 1); } } } else { i_skip = 6; while( i_skip < 23 && header[i_skip] == 0xff ) { i_skip++; } if( i_skip == 23 ) { printf( "!!! too much MPEG-1 stuffing" ); block_ChainRelease( p_pes ); return; } if( ( header[i_skip] & 0xC0 ) == 0x40 ) { i_skip += 2; } if( header[i_skip]&0x20 ) { i_pts = ((DS_S64)(header[i_skip]&0x0e ) << 29)| (DS_S64)(header[i_skip+1] << 22)| ((DS_S64)(header[i_skip+2]&0xfe) << 14)| (DS_S64)(header[i_skip+3] << 7)| (DS_S64)(header[i_skip+4] >> 1); if( header[i_skip]&0x10 ) /* has dts */ { i_dts = ((DS_S64)(header[i_skip+5]&0x0e ) << 29)| (DS_S64)(header[i_skip+6] << 22)| ((DS_S64)(header[i_skip+7]&0xfe) << 14)| (DS_S64)(header[i_skip+8] << 7)| (DS_S64)(header[i_skip+9] >> 1); i_skip += 10; } else { i_skip += 5; } } else { i_skip += 1; } } break; } if ( pid->b_stream_type == AC3_AUDIO_STREAM || pid->b_stream_type == MPEG2_AUDIO_STREAM ) { i_skip += 0; } #if 0 else if( pid->es->fmt.i_codec == VLC_FOURCC( 'l', 'p', 'c', 'b' ) || pid->es->fmt.i_codec == VLC_FOURCC( 's', 'p', 'u', 'b' ) || pid->es->fmt.i_codec == VLC_FOURCC( 's', 'd', 'd', 'b' ) ) { i_skip += 1; } #endif else if( pid->b_stream_type == SUBTITLE_STREAM && pid->es->p_mpeg4desc ) { decoder_config_descriptor_t *dcd = &pid->es->p_mpeg4desc->dec_descr; if( dcd->i_decoder_specific_info_len > 2 && dcd->p_decoder_specific_info[0] == 0x10 && ( dcd->p_decoder_specific_info[1]&0x10 ) ) { /* display length */ if( p_pes->i_buffer + 2 <= i_skip ) { i_length = GetWBE( &p_pes->p_buffer[i_skip] ); } i_skip += 2; } if( p_pes->i_buffer + 2 <= i_skip ) { i_pes_size = GetWBE( &p_pes->p_buffer[i_skip] ); } /* */ i_skip += 2; } //if ( pid->b_stream_type == MPEG2_VIDEO_STREAM) //if (i_skip) // printf("i_skip: %d\n", i_skip); /* skip header */ while( p_pes && i_skip > 0 ) { if( p_pes->i_buffer <= i_skip ) { block_t *p_next = p_pes->p_next; i_skip -= p_pes->i_buffer; block_Release( p_pes ); p_pes = p_next; } else { p_pes->i_buffer -= i_skip; p_pes->p_buffer += i_skip; break; } } /* ISO/IEC 13818-1 2.7.5: if no pts and no dts, then dts == pts */ if( i_pts >= 0 && i_dts < 0 ) i_dts = i_pts; if( p_pes ) { block_t *p_block; // int i; //printf("%lx, %lx\n", i_dts, i_pts); if( i_dts >= 0 ) { p_pes->i_dts = i_dts * 100 / 9; } if( i_pts >= 0 ) { p_pes->i_pts = i_pts * 100 / 9; } p_pes->i_length = i_length * 100 / 9; p_block = block_ChainGather( p_pes ); #if 0 if( pid->es->fmt.i_codec == VLC_FOURCC( 's', 'u', 'b', 't' ) ) { if( i_pes_size > 0 && p_block->i_buffer > i_pes_size ) { p_block->i_buffer = i_pes_size; } /* Append a \0 */ p_block = block_Realloc( p_block, 0, p_block->i_buffer + 1 ); p_block->p_buffer[p_block->i_buffer -1] = '\0'; } // // TBD: PES¸¦ ±×³É ³»º¸³»¾ß ÇÒ °æ¿ì... // for( i = 0; i < pid->i_extra_es; i++ ) { es_out_Send( pid->b_stream_type, block_Duplicate( p_block ) ); } #endif es_out_Send( pid, pid->b_stream_type, p_block ); } else { if (pd_dmx_dbg) printf("empty pes" ); } } static DS_BOOL GatherPES( demux_sys_t *p_demux, ts_pid_t *pid, block_t *p_bk ) { const DS_U8 *p = p_bk->p_buffer; int i_buffer = 0; const DS_U8 b_unit_start = p[1]&0x40; const DS_U8 b_adaptation = p[3]&0x20; const DS_U8 b_payload = p[3]&0x10; const int i_cc = p[3]&0x0f; /* continuity counter */ DS_BOOL b_discontinuity = DS_FALSE; /* discontinuity */ DS_BOOL b_have_to_drop = DS_FALSE; /* transport_scrambling_control is ignored */ int i_skip = 0; DS_BOOL i_ret = DS_FALSE; int i_diff; /* For now, ignore additional error correction * TODO: handle Reed-Solomon 204,188 error correction */ //p_bk->i_buffer = TS_PACKET_SIZE_188; i_buffer = TS_PACKET_SIZE_188; if( p[1]&0x80 ) { printf( "transport_error_indicator set (pid=%d)", pid->i_pid ); if( pid->es->p_pes ) //&& pid->es->fmt.i_cat == VIDEO_ES ) pid->es->p_pes->i_flags |= BLOCK_FLAG_CORRUPTED; //b_have_to_drop = DS_TRUE; } if( !b_adaptation ) { /* We don't have any adaptation_field, so payload starts * immediately after the 4 byte TS header */ i_skip = 4; } else { /* p[4] is adaptation_field_length minus one */ i_skip = 5 + p[4]; if( p[4] > 0 ) { /* discontinuity indicator found in stream */ b_discontinuity = (p[5]&0x80) ? DS_TRUE : DS_FALSE; if( b_discontinuity && pid->es->p_pes ) { if ( pd_dmx_dbg ) printf( "discontinuity indicator (pid=%d) ", pid->i_pid ); /* pid->es->p_pes->i_flags |= BLOCK_FLAG_DISCONTINUITY; */ } } } /* Test continuity counter */ /* continuous when (one of this): * diff == 1 * diff == 0 and payload == 0 * diff == 0 and duplicate packet (playload != 0) <- should we * test the content ? */ i_diff = ( i_cc - pid->i_cc )&0x0f; if( b_payload && i_diff == 1 ) { pid->i_cc = ( pid->i_cc + 1 ) & 0xf; } else { if( pid->i_cc == 0xff ) { if ( pd_dmx_dbg ) printf( "first packet for pid=%d cc=0x%x", pid->i_pid, i_cc ); pid->i_cc = i_cc; } else if( i_diff != 0 && !b_discontinuity ) { if ( pd_dmx_dbg ) printf( "discontinuity received 0x%x instead of 0x%x (pid=%d)", i_cc, ( pid->i_cc + 1 )&0x0f, pid->i_pid ); pid->i_cc = i_cc; if( pid->es->p_pes && pid->b_stream_type != MPEG2_VIDEO_STREAM ) { /* Small video artifacts are usually better then * dropping full frames */ pid->es->p_pes->i_flags |= BLOCK_FLAG_CORRUPTED; } } } PCRHandle( p_demux, pid, p_bk->p_buffer ); if( i_skip >= 188 || b_have_to_drop ) { block_Release( p_bk ); return i_ret; } /* We have to gather it */ p_bk->p_buffer += i_skip; p_bk->i_buffer -= i_skip; if( b_unit_start ) { // // ÀÌÀü¿¡ PES¸¦ ¹Þ´ø °ÍÀÌ ÀÖÀ¸¸é, ParsePES·Î º¸³½´Ù. // if( pid->es->p_pes ) { ParsePES( p_demux, pid ); i_ret = DS_TRUE; } block_ChainLastAppend( &pid->es->pp_last, p_bk ); if( p_bk->i_buffer > 6 ) { pid->es->i_pes_size = GetWBE( &p_bk->p_buffer[4] ); if( pid->es->i_pes_size > 0 ) { pid->es->i_pes_size += 6; } } pid->es->i_pes_gathered += p_bk->i_buffer; if( pid->es->i_pes_size > 0 && pid->es->i_pes_gathered >= pid->es->i_pes_size ) { ParsePES( p_demux, pid ); i_ret = DS_TRUE; } } else { if( pid->es->p_pes == NULL ) { if (pd_dmx_dbg>2) printf("broken packet\n"); block_Release( p_bk ); } else { block_ChainLastAppend( &pid->es->pp_last, p_bk ); pid->es->i_pes_gathered += p_bk->i_buffer; if( pid->es->i_pes_size > 0 && pid->es->i_pes_gathered >= pid->es->i_pes_size ) { ParsePES( p_demux, pid ); i_ret = DS_TRUE; } } } return i_ret; } static void GatherSections(dvbpsi_decoder_t* p_decoder, dvbpsi_psi_section_t* p_section) { PD_SECFILTER *p_cursec = (PD_SECFILTER *)NULL; ts_pid_t *pid; uint16_t i_pid; int i; DS_BOOL bFound = DS_FALSE; register DS_U8 b1, b2; i_pid = p_decoder->i_pid; SysASSERT( (i_pid<=8192) ); pid = &p_DmxSys->pid[i_pid]; /* * Find the appropriate filter. */ for (p_cursec = pid->p_sec; p_cursec; p_cursec = p_cursec->pNext) { if (p_cursec->bEnable == DS_FALSE) continue; for (i=0; ii_filter_size; i++) { b1 = (p_cursec->FilterBytes[i] & p_cursec->FilterMask[i]); b2 = (p_section->p_data[i] & p_cursec->FilterMask[i]); if ( b1 != b2 ) break; } if (i == p_cursec->i_filter_size) { bFound = DS_TRUE; break; } } if ( bFound == DS_TRUE ) { if ( s_SecCbFunc ) s_SecCbFunc( p_cursec, p_section->p_data, p_section->i_length + 4); } else { /* do nothing. */ } } #if 0 ___Demux_Main___() #endif /***************************************************************************** * Init *****************************************************************************/ static int Init( demux_sys_t *p_sys ) { int i; // ts_pid_t *pat; // const char *psz_mode; // DS_BOOL b_append; // vlc_value_t val; //p_sys = &s_DemuxSys; //memset( p_sys, 0, sizeof( demux_sys_t ) ); p_sys->i_firstread = 0; p_sys->i_nread = 0; p_sys->b_force = DS_FALSE; p_sys->i_filesize = 0; p_sys->i_cur_pcr = 0; p_sys->i_cur_pcr_offset = 0; p_sys->i_prev_pcr = 0; p_sys->i_prev_pcr_offset = 0; p_sys->i_first_pcr = 0; p_sys->i_first_pcr_offset = 0; //vlc_mutex_init( &p_sys->csa_lock ); /* Fill dump mode fields */ p_sys->i_write = 0; p_sys->p_file = NULL; p_sys->b_file_out = DS_FALSE; /* Init p_sys field */ p_sys->b_meta = DS_TRUE; p_sys->b_dvb_control = DS_TRUE; p_sys->i_dvb_program = 0; p_sys->i_dvb_start = 0; p_sys->i_dvb_length = 0; for( i = 0; i < 8192; i++ ) { ts_pid_t *pid = &p_sys->pid[i]; pid->i_pid = i; pid->b_seen = DS_FALSE; pid->b_valid = DS_FALSE; pid->i_owner_number = 0; DMX_LIST_INIT( pid->p_sec ); pid->h_dvbpsi = dvbpsi_AttachSection( (dvbpsi_callback)GatherSections, NULL, i ); //pid->p_sec = (PD_SECFILTER *)NULL; } /* PID 8191 is padding */ p_sys->pid[8191].b_seen = DS_TRUE; p_sys->b_udp_out = DS_FALSE; p_sys->i_ts_read = 50; /* Init PMT array */ p_sys->i_pmt = 0; p_sys->pmt = NULL; p_sys->b_silent = DS_TRUE; return PD_DMX_SUCCESS; } static int Open( demux_sys_t *p_sys ) { const DS_U8 *p_peek; int i_peek = 0, i_sync = 0; int i_packet_size; DS_BOOL b_topfield = DS_FALSE; int retry_cnt=0; // // TBD: Packet rate shall be calculated on the incoming file. // p_sys->i_packet_rate = STREAM_BITRATE_DEF/8; if( stream_Peek( p_sys, &p_peek, TS_PACKET_SIZE_MAX*8 ) < TS_PACKET_SIZE_MAX*8 ) return PD_DMX_EGENERIC; if( memcmp( p_peek, "TFrc", 4 ) == 0 ) { b_topfield = DS_TRUE; if ( pd_dmx_dbg ) printf( "this is a topfield file\n" ); } i_sync = 0; retry_label: if (i_sync >= (TS_PACKET_SIZE_MAX*8)) { printf( "TS module discarded (lost sync)\n" ); return PD_DMX_EGENERIC; } /* Search first sync byte */ for( ; i_sync < (TS_PACKET_SIZE_MAX*8); i_sync++ ) { if( p_peek[i_sync] == 0x47 ) break; } if( i_sync >= (TS_PACKET_SIZE_MAX*8) && !b_topfield ) { if( !p_sys->b_force ) { if ( pd_dmx_dbg ) printf( "this does not look like a TS stream\n" ); return PD_DMX_EGENERIC; } if ( pd_dmx_dbg ) printf( "this does not look like a TS stream, continuing\n" ); } if( b_topfield ) { /* Read the entire Topfield header */ i_peek = TS_TOPFIELD_HEADER; } else { /* Check next 3 sync bytes */ i_peek = TS_PACKET_SIZE_MAX * (8+3) + i_sync + 1; } if( ( stream_Peek( p_sys, &p_peek, i_peek ) ) < i_peek ) { if ( pd_dmx_dbg ) printf( "cannot peek\n" ); return PD_DMX_EGENERIC; } //dump(p_peek); if( p_peek[i_sync + TS_PACKET_SIZE_188] == 0x47 && p_peek[i_sync + 2 * TS_PACKET_SIZE_188] == 0x47 && p_peek[i_sync + 3 * TS_PACKET_SIZE_188] == 0x47 ) { i_packet_size = TS_PACKET_SIZE_188; } else if( p_peek[i_sync + TS_PACKET_SIZE_192] == 0x47 && p_peek[i_sync + 2 * TS_PACKET_SIZE_192] == 0x47 && p_peek[i_sync + 3 * TS_PACKET_SIZE_192] == 0x47 ) { i_packet_size = TS_PACKET_SIZE_192; } else if( p_peek[i_sync + TS_PACKET_SIZE_196] == 0x47 && p_peek[i_sync + 2 * TS_PACKET_SIZE_196] == 0x47 && p_peek[i_sync + 3 * TS_PACKET_SIZE_196] == 0x47 ) { i_packet_size = TS_PACKET_SIZE_196; } else if( p_peek[i_sync + TS_PACKET_SIZE_204] == 0x47 && p_peek[i_sync + 2 * TS_PACKET_SIZE_204] == 0x47 && p_peek[i_sync + 3 * TS_PACKET_SIZE_204] == 0x47 ) { i_packet_size = TS_PACKET_SIZE_204; } else if( p_sys->b_force ) { i_packet_size = TS_PACKET_SIZE_188; } else if( b_topfield ) { i_packet_size = TS_PACKET_SIZE_188; } else { if (retry_cnt++<8) { printf("retry! %d\n", retry_cnt); i_sync++; goto retry_label; } if ( pd_dmx_dbg ) printf( "TS module discarded (lost sync)\n" ); return PD_DMX_EGENERIC; } p_sys->i_packet_size = i_packet_size; if ( p_sys->buffer == (DS_U8 *)NULL ) p_sys->buffer = (DS_U8 *)malloc(TS_PACKET_SIZE_204); SysASSERT( p_sys->buffer ); p_sys->i_nread = 0; p_sys->i_first_pcr_offset = p_sys->i_cur_pcr_offset = p_sys->i_prev_pcr_offset = 0; p_sys->i_first_pcr = p_sys->i_cur_pcr = p_sys->i_prev_pcr = 0; p_sys->i_filesize = stream_Size(p_sys); if ( pd_dmx_dbg ) printf("[PD-INFO] TS packet size: %d\n", i_packet_size ); return PD_DMX_SUCCESS; } /***************************************************************************** * Close *****************************************************************************/ static int Close( demux_sys_t *p_sys ) { int retVal = PD_DMX_SUCCESS; int i; if ( pd_dmx_dbg ) printf( "pid list:" ); for( i = 0; i < 8192; i++ ) { ts_pid_t *pid = &p_sys->pid[i]; #if 0 if( pid->b_valid && pid->psi ) { switch( pid->i_pid ) { case 0: /* PAT */ dvbpsi_DetachPAT( pid->psi->handle ); free( pid->psi ); break; case 1: /* CAT */ free( pid->psi ); break; case 0x11: /* SDT */ case 0x12: /* EIT */ dvbpsi_DetachDemux( pid->psi->handle ); free( pid->psi ); break; default: PIDClean( p_demux->out, pid ); break; } } else if( pid->b_valid && pid->es ) { PIDClean( p_demux->out, pid ); } #endif if( pid->b_seen ) { if ( pd_dmx_dbg ) printf( " - pid[%d] seen\n", pid->i_pid ); } } #if 0 if( p_sys->b_udp_out ) { net_Close( p_sys->fd ); } if( p_sys->csa ) { var_DelCallback( p_demux, "ts-csa-ck", ChangeKeyCallback, NULL ); var_DelCallback( p_demux, "ts-csa2-ck", ChangeKeyCallback, NULL ); csa_Delete( p_sys->csa ); p_sys->csa = NULL; } if( p_sys->i_pmt ) free( p_sys->pmt ); if ( p_sys->p_programs_list ) { vlc_value_t val; val.p_list = p_sys->p_programs_list; var_Change( p_demux, "programs", VLC_VAR_FREELIST, &val, NULL ); } /* If in dump mode, then close the file */ if( p_sys->b_file_out ) { if ( pd_dmx_dbg ) printf( "closing %s (%"PRId64" Kbytes dumped)\n", p_sys->psz_file, p_sys->i_write / 1024 ); if( p_sys->p_file != stdout ) { fclose( p_sys->p_file ); p_sys->p_file = NULL; } } if ( p_sys->buffer ) free( p_sys->buffer ); free( p_sys->psz_file ); p_sys->psz_file = NULL; //vlc_mutex_destroy( &p_sys->csa_lock ); free( p_sys ); #endif if ( p_sys->p_infile ) fclose( p_sys->p_infile ); p_sys->p_infile = (FILE *)NULL; return retVal; } static int Control( demux_sys_t *p_sys, int i_query, va_list args ) { double f, *pf; DS_S64 i64; //DS_S64 *pi64; //int i_int; switch( i_query ) { case DEMUX_GET_POSITION: pf = (double*) va_arg( args, double* ); i64 = stream_Size( p_sys ); if( i64 > 0 ) { *pf = (double)stream_Tell( p_sys ) / (double)i64; } return PD_DMX_SUCCESS; case DEMUX_SET_POSITION: f = (double) va_arg( args, double ); i64 = stream_Size( p_sys ); //es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); if( stream_Seek( p_sys, (DS_S64)(i64 * f) ) ) { return PD_DMX_EGENERIC; } return PD_DMX_SUCCESS; case DEMUX_GET_FPS: case DEMUX_SET_TIME: default: return PD_DMX_EGENERIC; } } /***************************************************************************** * Demux: *****************************************************************************/ static int Demux( demux_sys_t *p_sys ) { int i_pkt; // PD_SECFILTER *p_cursec = (PD_SECFILTER *)NULL; /* We read at most 100 TS packet or until a frame is completed */ for( i_pkt = 0; i_pkt < p_sys->i_ts_read; i_pkt++ ) { DS_BOOL b_frame = DS_FALSE; block_t *p_pkt; ts_pid_t *p_pid; /* Get a new TS packet */ if( !( p_pkt = stream_Block( p_sys, p_sys->i_packet_size ) ) ) { //if ( pd_dmx_dbg ) printf( "eof ?\n" ); return -1; } /* Check sync byte and re-sync if needed */ if( p_pkt->p_buffer[0] != 0x47 ) { if ( pd_dmx_dbg ) printf( "lost synchro\n" ); block_Release( p_pkt ); while( 1 ) { const DS_U8 *p_peek; int i_peek, i_skip = 0; i_peek = stream_Peek( p_sys, &p_peek, p_sys->i_packet_size * 10 ); if( i_peek < p_sys->i_packet_size + 1 ) { //if ( pd_dmx_dbg ) printf( "eof ?\n" ); return -1; } while( i_skip < i_peek - p_sys->i_packet_size ) { if( p_peek[i_skip] == 0x47 && p_peek[i_skip + p_sys->i_packet_size] == 0x47 ) { break; } i_skip++; } if ( pd_dmx_dbg ) printf( "skipping %d bytes of garbage\n", i_skip ); stream_Read( p_sys, NULL, i_skip ); if( i_skip < i_peek - p_sys->i_packet_size ) { break; } } if( !( p_pkt = stream_Block( p_sys, p_sys->i_packet_size ) ) ) { //if ( pd_dmx_dbg ) printf( "eof ?\n" ); return -1; } } /* Parse the TS packet */ p_pid = &p_sys->pid[PIDGet( &p_pkt->p_buffer[0] )]; LOCK_PD_DMX_MUTEX(); /* * If PID filter is set (p_pid->b_valid == TRUE), * then we shall push the packet to upper layer. */ if( p_pid->b_valid ) { if( p_pid->p_sec ) { #if 0 if( p_pid->i_pid == 0 || p_pid->i_pid == 0x11 || p_pid->i_pid == 0x12 ) { dvbpsi_PushPacket( p_pid->psi->handle, p_pkt->p_buffer ); } else { int i_prg; for( i_prg = 0; i_prg < p_pid->psi->i_prg; i_prg++ ) { dvbpsi_PushPacket( p_pid->psi->prg[i_prg]->handle, p_pkt->p_buffer ); } } // block_Release( p_pkt ); #else DS_BOOL bFound; bFound = DS_FALSE; #if 0 if (p_pkt->p_buffer[1] & 0x40/*payload_unit_start_indicator*/) { PD_SECFILTER *p_cursec; DS_U8 b1, b2; int payload_offset; int adaptation_field; int i; adaptation_field = (p_pkt->p_buffer[3]>>4) & 0x03; if ( adaptation_field == 1 /* payload only */ ) payload_offset = 4; else if ( adaptation_field == 3 || adaptation_field == 2 ) payload_offset = 4+p_pkt->p_buffer[4]; else printf("!!! adaption_field is invalid! %d\n", adaptation_field); printf("ad: %d\n", adaptation_field); for (p_cursec = p_pid->p_sec; p_cursec; p_cursec = p_cursec->pNext) { if (p_cursec->bEnable == DS_FALSE) continue; for (i=0; ii_filter_size; i++) { b1 = (p_cursec->FilterBytes[i] & p_cursec->FilterMask[i]); b2 = (p_pkt->p_buffer[payload_offset+i+1] & p_cursec->FilterMask[i]); if ( b1 != b2 ) break; } if (i == p_cursec->i_filter_size) { bFound = DS_TRUE; break; } } } if (bFound) #endif { /* * Do section filter. */ dvbpsi_PushPacket( p_pid->h_dvbpsi, p_pkt->p_buffer ); block_Release( p_pkt ); } #endif } else if( !p_sys->b_udp_out ) { b_frame = GatherPES( p_sys, p_pid, p_pkt ); /* * Do PID filter for A/V. */ // // PES¿¡¼­ ÇØ´ç ÆÐŶÀ» ¾µ¾¥µµ ÀÖÀ¸´Ï ¸±¸®Áî ÇÏÁö ¸»ÀÚ. // } else { PCRHandle( p_sys, p_pid, p_pkt->p_buffer ); block_Release( p_pkt ); } } else { if( !p_pid->b_seen ) { //if ( pd_dmx_dbg ) printf( "pid[%d] unknown\n", p_pid->i_pid ); } /* We have to handle PCR if present */ PCRHandle( p_sys, p_pid, p_pkt->p_buffer ); block_Release( p_pkt ); } p_pid->b_seen = DS_TRUE; UNLOCK_PD_DMX_MUTEX(); if( b_frame ) { break; } } done: return 1; } #if 0 ___DEBUG_APIs___() #endif void pset_dmx_dbg(int bDebug) { pd_dmx_dbg = bDebug; } void pdmx_info(void) { if (p_DmxSys->p_infile) printf("Stream filename: %s\n", p_DmxSys->psz_infile ); else { printf("Stream filename: not specified or cannot open.\n"); return; } printf("Stream bitrate: %ld.%ld (long-term)\n", (p_DmxSys->i_packet_rate*8)/1000000, (p_DmxSys->i_packet_rate*8)%1000000); printf("Stream bitrate: %ld.%ld (short-term)\n", (p_DmxSys->i_packet_rate_short*8)/1000000, (p_DmxSys->i_packet_rate_short*8)%1000000); printf("Read offset: %lld\n", p_DmxSys->i_nread); printf("TS packet size: %d\n", p_DmxSys->i_packet_size); printf("TS file size: %lld\n", p_DmxSys->i_filesize); if (p_DmxSys->i_packet_rate) printf("Playtime (current/total): %lld / %lld\n", (p_DmxSys->i_nread / p_DmxSys->i_packet_rate), (p_DmxSys->i_filesize / p_DmxSys->i_packet_rate)); else printf("Playtime cannot be calculated since packet_rate is not calculated yet.\n"); } void change_file(int num, int bReadList, int bForce, int bCont) { static int b_list_read = 0; static char filename[256][256]; DS_BOOL b_demux_started; char *p; int i; FILE *fp; if ( b_list_read == 0 || bReadList ) { fp = fopen("list.txt", "r"); if ( !fp ) { printf("Cannot find list file; list.txt\n"); b_list_read = 0; return; } for (i=0; i<256; i++) { p=fgets( filename[i], 255, fp ); if (p==NULL) break; } fclose(fp); b_list_read = i; } if ( num >= b_list_read ) { printf("Maximum filelist count is %d.\n", num); return; } if ( num == -1 ) { printf("Stop the demux.\n"); PD_DMX_StopDemux(); } printf("Change stream file to (%d) %s\n", num, filename[num]); if ( filename[num][strlen(filename[num])-1] == '\r' || filename[num][strlen(filename[num])-1] == '\n' ) filename[num][strlen(filename[num])-1] = '\0'; if ( filename[num][strlen(filename[num])-1] == '\r' || filename[num][strlen(filename[num])-1] == '\n' ) filename[num][strlen(filename[num])-1] = '\0'; b_demux_started = PD_DMX_IsDemuxStarted(); if ( b_demux_started ) PD_DMX_StopDemux(); PD_DMX_SetDemuxFile(filename[num], bCont); if ( b_demux_started ) PD_DMX_StartDemux(); } void set_output_file(int num) { PD_DMX_SetOutputFile("./video.vid", "./audio.ac3"); } void do_dmx_control(int num) { Control( p_DmxSys, 0, NULL ); }