diff options
author | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-04-18 22:33:39 +0000 |
---|---|---|
committer | Daniel Caujolle-Bert <f1rmb@users.sourceforge.net> | 2001-04-18 22:33:39 +0000 |
commit | db6b7c2e1c52c536a7f9690a410bf69817e0b2c5 (patch) | |
tree | fdaf7537abca3d4875ad21322c54888914ed15a2 /src/demuxers | |
download | xine-lib-db6b7c2e1c52c536a7f9690a410bf69817e0b2c5.tar.gz xine-lib-db6b7c2e1c52c536a7f9690a410bf69817e0b2c5.tar.bz2 |
Initial revision
CVS patchset: 1
CVS date: 2001/04/18 22:33:39
Diffstat (limited to 'src/demuxers')
-rw-r--r-- | src/demuxers/Makefile.am | 48 | ||||
-rw-r--r-- | src/demuxers/demux.h | 125 | ||||
-rw-r--r-- | src/demuxers/demux_avi.c | 982 | ||||
-rw-r--r-- | src/demuxers/demux_elem.c | 284 | ||||
-rw-r--r-- | src/demuxers/demux_mpeg.c | 626 | ||||
-rw-r--r-- | src/demuxers/demux_mpeg_block.c | 519 | ||||
-rw-r--r-- | src/demuxers/demux_mpgaudio.c | 440 |
7 files changed, 3024 insertions, 0 deletions
diff --git a/src/demuxers/Makefile.am b/src/demuxers/Makefile.am new file mode 100644 index 000000000..46e5bc8b6 --- /dev/null +++ b/src/demuxers/Makefile.am @@ -0,0 +1,48 @@ +CFLAGS = @BUILD_LIB_STATIC@ @GLOBAL_CFLAGS@ + +noinst_LTLIBRARIES = libdemux.la + +#libdemux_la_SOURCES = demux_avi.c demux_mpeg_block.c demux_mpeg.c \ +# demux_mpgaudio.c demux_elem.c +libdemux_la_SOURCES = demux_avi.c demux_mpeg_block.c demux_mpeg.c +#libdemux_la_DEPENDENCIES = libsdeps +#libdemux_la_LIBADD = $(top_builddir)/libmpg123/libmpg123.la + +include_HEADERS = demux.h + +#libsdeps: +# @cd $(top_builddir)/libmpg123 && $(MAKE) libmpg123.la + +## +## Install header files (default=$includedir/xine) +## +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(includedir)/xine + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \ + echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/xine/$$p"; \ + $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/xine/$$p; \ + done + + +## +## Remove them +## +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + list='$(include_HEADERS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(includedir)/xine/$$p; \ + done + +debug: + $(MAKE) CFLAGS="$(DEBUG_CFLAGS)" + +mostlyclean-generic: + -rm -f *~ \#* .*~ .\#* + +maintainer-clean-generic: + -@echo "This command is intended for maintainers to use;" + -@echo "it deletes files that may require special tools to rebuild." + -rm -f Makefile.in + diff --git a/src/demuxers/demux.h b/src/demuxers/demux.h new file mode 100644 index 000000000..6fda091fe --- /dev/null +++ b/src/demuxers/demux.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix 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: demux.h,v 1.1 2001/04/18 22:33:54 f1rmb Exp $ + */ + +#ifndef HAVE_DEMUX_H +#define HAVE_DEMUX_H + +#include "buffer.h" +#include "xine.h" +#if defined(XINE_COMPILE) +#include "input/input_plugin.h" +#else +#include "input_plugin.h" +#endif + +#define DEMUX_OK 0 +#define DEMUX_FINISHED 1 + +#define DEMUX_CANNOT_HANDLE 0 +#define DEMUX_CAN_HANDLE 1 + +#define DEMUX_DEFAULT_STRATEGY 0 +#define DEMUX_REVERT_STRATEGY 1 +#define DEMUX_CONTENT_STRATEGY 2 +#define DEMUX_EXTENSION_STRATEGY 3 + +#define STAGE_BY_CONTENT 1 +#define STAGE_BY_EXTENSION 2 + +/* + * a demux plugin (no matter if it's staically built into xine + * or dynamically loaded at run-time) must implement these functions + */ + +typedef struct demux_plugin_s demux_plugin_t; + +struct demux_plugin_s +{ + /* + * ask demuxer to open the given stream (input-plugin) + * using the content-detection method specified in <stage> + * + * return values: + * DEMUX_CAN_HANDLE on success + * DEMUX_CANNOT_HANDLE on failure + */ + + int (*open) (demux_plugin_t *this, input_plugin_t *ip, + int stage); + + /* + * start demux thread + * pos : 0..65535 + */ + + void (*start) (demux_plugin_t *this, fifo_buffer_t *video_fifo, + fifo_buffer_t *audio_fifo, fifo_buffer_t *spu_fifo, + off_t pos) ; + + /* + * stop & kill demux thread, free resources associated with current + * input stream + */ + + void (*stop) (demux_plugin_t *this) ; + + /* + * close demuxer, free all resources + */ + + void (*close) (demux_plugin_t *this) ; + + /* + * returns DEMUX_OK or DEMUX_FINISHED + */ + + int (*get_status) (demux_plugin_t *this) ; + + /* + * return human readable identifier for this plugin + */ + + char* (*get_identifier) (demux_plugin_t *this); + +} ; + +/* + * for dynamic demux plugins: + * + * make sure you provide this (and only this!) function call: + * + * demux_plugin_t *init_demux_plugin (config_values_t *cfg, uint32_t xd); + * + */ + +demux_plugin_t *init_demux_mpeg (config_values_t *cfg, uint32_t xd); + +demux_plugin_t *init_demux_mpeg_block (config_values_t *cfg, uint32_t xd); + +demux_plugin_t *init_demux_avi (config_values_t *cfg, uint32_t xd); + +demux_plugin_t *init_demux_mpeg_audio (config_values_t *cfg, uint32_t xd); + +demux_plugin_t *init_demux_mpeg_elem(config_values_t *cfg, uint32_t xd); + +#endif + diff --git a/src/demuxers/demux_avi.c b/src/demuxers/demux_avi.c new file mode 100644 index 000000000..badcadc7d --- /dev/null +++ b/src/demuxers/demux_avi.c @@ -0,0 +1,982 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix 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: demux_avi.c,v 1.1 2001/04/18 22:33:55 f1rmb Exp $ + * + * demultiplexer for avi streams + * + * part of the code is taken from + * avilib (C) 1999 Rainer Johanni <Rainer@Johanni.de> + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> +#include <stdlib.h> + +#include "xine.h" +#include "monitor.h" +#include "demux.h" +#include "utils.h" +#include "libw32dll/wine/mmreg.h" +#include "libw32dll/wine/avifmt.h" +#include "libw32dll/wine/vfw.h" + +/* The following variable indicates the kind of error */ + +static uint32_t xine_debug; + +typedef struct +{ + long pos; + long len; +} video_index_entry_t; + +typedef struct +{ + long pos; + long len; + long tot; +} audio_index_entry_t; + +typedef struct +{ + long width; /* Width of a video frame */ + long height; /* Height of a video frame */ + long dwScale, dwRate; + double fps; /* Frames per second */ + + char compressor[8]; /* Type of compressor, 4 bytes + padding for 0 byte */ + long video_strn; /* Video stream number */ + long video_frames; /* Number of video frames */ + char video_tag[4]; /* Tag of video data */ + long video_posf; /* Number of next frame to be read + (if index present) */ + long video_posb; /* Video position: byte within frame */ + + long a_fmt; /* Audio format, see #defines below */ + long a_chans; /* Audio channels, 0 for no audio */ + long a_rate; /* Rate in Hz */ + long a_bits; /* bits per audio sample */ + long audio_strn; /* Audio stream number */ + long audio_bytes; /* Total number of bytes of audio data */ + long audio_chunks; /* Chunks of audio data in the file */ + char audio_tag[4]; /* Tag of audio data */ + long audio_posc; /* Audio position: chunk */ + long audio_posb; /* Audio position: byte within chunk */ + + long pos; /* position in file */ + long n_idx; /* number of index entries actually filled */ + long max_idx; /* number of index entries actually allocated */ + unsigned char (*idx)[16]; /* index entries (AVI idx1 tag) */ + video_index_entry_t *video_index; + audio_index_entry_t *audio_index; + BITMAPINFOHEADER bih; + char wavex[64]; + off_t movi_start; + uint32_t AVI_errno; +} avi_t; + +typedef struct demux_avi_s { + demux_plugin_t demux_plugin; + + fifo_buffer_t *audio_fifo; + fifo_buffer_t *video_fifo; + + input_plugin_t *input; + + avi_t *avi; + + pthread_t thread; + + int status; + + uint32_t video_step; + uint32_t avg_bytes_per_sec; +} demux_avi_t ; + +#define AVI_ERR_SIZELIM 1 /* The write of the data would exceed + the maximum size of the AVI file. + This is more a warning than an error + since the file may be closed safely */ + +#define AVI_ERR_OPEN 2 /* Error opening the AVI file - wrong path + name or file nor readable/writable */ + +#define AVI_ERR_READ 3 /* Error reading from AVI File */ + +#define AVI_ERR_WRITE 4 /* Error writing to AVI File, + disk full ??? */ + +#define AVI_ERR_WRITE_INDEX 5 /* Could not write index to AVI file + during close, file may still be + usable */ + +#define AVI_ERR_CLOSE 6 /* Could not write header to AVI file + or not truncate the file during close, + file is most probably corrupted */ + +#define AVI_ERR_NOT_PERM 7 /* Operation not permitted: + trying to read from a file open + for writing or vice versa */ + +#define AVI_ERR_NO_MEM 8 /* malloc failed */ + +#define AVI_ERR_NO_AVI 9 /* Not an AVI file */ + +#define AVI_ERR_NO_HDRL 10 /* AVI file has no has no header list, + corrupted ??? */ + +#define AVI_ERR_NO_MOVI 11 /* AVI file has no has no MOVI list, + corrupted ??? */ + +#define AVI_ERR_NO_VIDS 12 /* AVI file contains no video data */ + +#define AVI_ERR_NO_IDX 13 /* The file has been opened with + getIndex==0, but an operation has been + performed that needs an index */ + +static unsigned long str2ulong(unsigned char *str) +{ + return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) ); +} + +static unsigned long str2ushort(unsigned char *str) +{ + return ( str[0] | (str[1]<<8) ); +} + +static void long2str(unsigned char *dst, int n) +{ + dst[0] = (n )&0xff; + dst[1] = (n>> 8)&0xff; + dst[2] = (n>>16)&0xff; + dst[3] = (n>>24)&0xff; +} + +static void AVI_close(avi_t *AVI) +{ + if(AVI->idx) free(AVI->idx); + if(AVI->video_index) free(AVI->video_index); + if(AVI->audio_index) free(AVI->audio_index); + free(AVI); +} + +#define ERR_EXIT(x) \ +{ \ + AVI->AVI_errno = x; \ + return 0; \ +} + +#define PAD_EVEN(x) ( ((x)+1) & ~1 ) + +static int avi_sampsize(avi_t *AVI) +{ + int s; + s = ((AVI->a_bits+7)/8)*AVI->a_chans; + if(s==0) s=1; /* avoid possible zero divisions */ + return s; +} + +static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, + long flags, long pos, long len) +{ + void *ptr; + + if(AVI->n_idx>=AVI->max_idx) + { + ptr = realloc((void *)AVI->idx,(AVI->max_idx+4096)*16); + if(ptr == 0) + { + AVI->AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + AVI->max_idx += 4096; + AVI->idx = (unsigned char((*)[16]) ) ptr; + } + + /* Add index entry */ + + memcpy(AVI->idx[AVI->n_idx],tag,4); + long2str(AVI->idx[AVI->n_idx]+ 4,flags); + long2str(AVI->idx[AVI->n_idx]+ 8,pos); + long2str(AVI->idx[AVI->n_idx]+12,len); + + /* Update counter */ + + AVI->n_idx++; + + return 0; +} + +static avi_t *AVI_init(demux_avi_t *this) +{ + avi_t *AVI; + long i, n, idx_type; + unsigned char *hdrl_data; + long hdrl_len=0; + long nvi, nai, ioff; + long tot; + int lasttag = 0; + int vids_strh_seen = 0; + int vids_strf_seen = 0; + int auds_strh_seen = 0; + int auds_strf_seen = 0; + int num_stream = 0; + char data[256]; + + /* Create avi_t structure */ + + AVI = (avi_t *) xmalloc(sizeof(avi_t)); + if(AVI==NULL) + { + AVI->AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + /* Read first 12 bytes and check that this is an AVI file */ + + if( this->input->read(data,12) != 12 ) ERR_EXIT(AVI_ERR_READ) ; + + if( strncasecmp(data ,"RIFF",4) !=0 || + strncasecmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI) ; + /* Go through the AVI file and extract the header list, + the start position of the 'movi' list and an optionally + present idx1 tag */ + + hdrl_data = 0; + + while(1) { + if (this->input->read(data,8) != 8 ) break; /* We assume it's EOF */ + + n = str2ulong(data+4); + n = PAD_EVEN(n); + + if(strncasecmp(data,"LIST",4) == 0) + { + if( this->input->read(data,4) != 4 ) ERR_EXIT(AVI_ERR_READ) + n -= 4; + if(strncasecmp(data,"hdrl",4) == 0) + { + hdrl_len = n; + hdrl_data = (unsigned char *) xmalloc(n); + if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM) + if( this->input->read(hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ) + } + else if(strncasecmp(data,"movi",4) == 0) + { + AVI->movi_start = this->input->seek(0,SEEK_CUR); + this->input->seek(n,SEEK_CUR); + } + else + this->input->seek(n,SEEK_CUR); + } + else if(strncasecmp(data,"idx1",4) == 0) + { + /* n must be a multiple of 16, but the reading does not + break if this is not the case */ + + AVI->n_idx = AVI->max_idx = n/16; + AVI->idx = (unsigned char((*)[16]) ) xmalloc(n); + if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM) + if( this->input->read((char *)AVI->idx,n) != n ) ERR_EXIT(AVI_ERR_READ) + } + else + this->input->seek(n,SEEK_CUR); + } + + if(!hdrl_data) ERR_EXIT(AVI_ERR_NO_HDRL) ; + if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI) ; + + /* Interpret the header list */ + + for(i=0;i<hdrl_len;) + { + /* List tags are completly ignored */ + + if(strncasecmp(hdrl_data+i,"LIST",4)==0) { i+= 12; continue; } + + n = str2ulong(hdrl_data+i+4); + n = PAD_EVEN(n); + + /* Interpret the tag and its args */ + + if(strncasecmp(hdrl_data+i,"strh",4)==0) + { + i += 8; + if(strncasecmp(hdrl_data+i,"vids",4) == 0 && !vids_strh_seen) + { + memcpy(AVI->compressor,hdrl_data+i+4,4); + AVI->compressor[4] = 0; + AVI->dwScale = str2ulong(hdrl_data+i+20); + AVI->dwRate = str2ulong(hdrl_data+i+24); + + if(AVI->dwScale!=0) + AVI->fps = (double)AVI->dwRate/(double)AVI->dwScale; + this->video_step = (long) (90000.0 / AVI->fps); + + AVI->video_frames = str2ulong(hdrl_data+i+32); + AVI->video_strn = num_stream; + vids_strh_seen = 1; + lasttag = 1; /* vids */ + } + else if (strncasecmp (hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen) + { + AVI->audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI); + AVI->audio_strn = num_stream; + auds_strh_seen = 1; + lasttag = 2; /* auds */ + } + else + lasttag = 0; + num_stream++; + } + else if(strncasecmp(hdrl_data+i,"strf",4)==0) + { + i += 8; + if(lasttag == 1) { + /* printf ("size : %d\n",sizeof(AVI->bih)); */ + memcpy (&AVI->bih, hdrl_data+i, sizeof(AVI->bih)); + /* stream_read(demuxer->stream,(char*) &avi_header.bih,MIN(size2,sizeof(avi_header.bih))); */ + AVI->width = str2ulong(hdrl_data+i+4); + AVI->height = str2ulong(hdrl_data+i+8); + + /* + printf ("size : %d x %d (%d x %d)\n", AVI->width, AVI->height, AVI->bih.biWidth, AVI->bih.biHeight); + printf(" biCompression %d='%.4s'\n", AVI->bih.biCompression, + &AVI->bih.biCompression); + */ + vids_strf_seen = 1; + } + else if(lasttag == 2) + { + memcpy (&AVI->wavex, hdrl_data+i, n); + + AVI->a_fmt = str2ushort(hdrl_data+i ); + AVI->a_chans = str2ushort(hdrl_data+i+2); + AVI->a_rate = str2ulong (hdrl_data+i+4); + AVI->a_bits = str2ushort(hdrl_data+i+14); + this->avg_bytes_per_sec = str2ulong (hdrl_data+i+8); + auds_strf_seen = 1; + } + lasttag = 0; + } + else + { + i += 8; + lasttag = 0; + } + + i += n; + } + + free(hdrl_data); + + if(!vids_strh_seen || !vids_strf_seen || AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS) + + AVI->video_tag[0] = AVI->video_strn/10 + '0'; + AVI->video_tag[1] = AVI->video_strn%10 + '0'; + AVI->video_tag[2] = 'd'; + AVI->video_tag[3] = 'b'; + + /* Audio tag is set to "99wb" if no audio present */ + if(!AVI->a_chans) AVI->audio_strn = 99; + + AVI->audio_tag[0] = AVI->audio_strn/10 + '0'; + AVI->audio_tag[1] = AVI->audio_strn%10 + '0'; + AVI->audio_tag[2] = 'w'; + AVI->audio_tag[3] = 'b'; + + this->input->seek(AVI->movi_start,SEEK_SET); + + /* if the file has an idx1, check if this is relative + to the start of the file or to the start of the movi list */ + + idx_type = 0; + + if(AVI->idx) + { + long pos, len; + + /* Search the first videoframe in the idx1 and look where + it is in the file */ + + for(i=0;i<AVI->n_idx;i++) + if( strncasecmp(AVI->idx[i],AVI->video_tag,3)==0 ) break; + if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS) + + pos = str2ulong(AVI->idx[i]+ 8); + len = str2ulong(AVI->idx[i]+12); + + this->input->seek(pos,SEEK_SET); + if(this->input->read(data,8)!=8) ERR_EXIT(AVI_ERR_READ) ; + if( strncasecmp(data,AVI->idx[i],4)==0 && str2ulong(data+4)==len ) + { + idx_type = 1; /* Index from start of file */ + } + else + { + this->input->seek(pos+AVI->movi_start-4,SEEK_SET); + if(this->input->read(data,8)!=8) ERR_EXIT(AVI_ERR_READ) ; + if( strncasecmp(data,AVI->idx[i],4)==0 && str2ulong(data+4)==len ) + { + idx_type = 2; /* Index from start of movi list */ + } + } + /* idx_type remains 0 if neither of the two tests above succeeds */ + } + + if(idx_type == 0) + { + /* we must search through the file to get the index */ + + this->input->seek( AVI->movi_start, SEEK_SET); + + AVI->n_idx = 0; + + while(1) + { + if( this->input->read(data,8) != 8 ) break; + n = str2ulong(data+4); + + /* The movi list may contain sub-lists, ignore them */ + + if(strncasecmp(data,"LIST",4)==0) + { + this->input->seek(4,SEEK_CUR); + continue; + } + + /* Check if we got a tag ##db, ##dc or ##wb */ + + if( ( (data[2]=='d' || data[2]=='D') && + (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) + || ( (data[2]=='w' || data[2]=='W') && + (data[3]=='b' || data[3]=='B') ) ) + { + avi_add_index_entry(AVI,data,0,this->input->seek(0,SEEK_CUR)-8,n); + } + + this->input->seek(PAD_EVEN(n),SEEK_CUR); + } + idx_type = 1; + } + + /* Now generate the video index and audio index arrays */ + + nvi = 0; + nai = 0; + + for(i=0;i<AVI->n_idx;i++) + { + if(strncasecmp(AVI->idx[i],AVI->video_tag,3) == 0) nvi++; + if(strncasecmp(AVI->idx[i],AVI->audio_tag,4) == 0) nai++; + } + + AVI->video_frames = nvi; + AVI->audio_chunks = nai; + + if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS) ; + + AVI->video_index = (video_index_entry_t *) xmalloc(nvi*sizeof(video_index_entry_t)); + if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM) ; + + if(AVI->audio_chunks) { + AVI->audio_index = (audio_index_entry_t *) xmalloc(nai*sizeof(audio_index_entry_t)); + if(AVI->audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM) ; + } + + nvi = 0; + nai = 0; + tot = 0; + ioff = idx_type == 1 ? 8 : AVI->movi_start+4; + + for(i=0;i<AVI->n_idx;i++) + { + if(strncasecmp(AVI->idx[i],AVI->video_tag,3) == 0) + { + AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff; + AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12); + nvi++; + } + if(strncasecmp(AVI->idx[i],AVI->audio_tag,4) == 0) + { + AVI->audio_index[nai].pos = str2ulong(AVI->idx[i]+ 8)+ioff; + AVI->audio_index[nai].len = str2ulong(AVI->idx[i]+12); + AVI->audio_index[nai].tot = tot; + tot += AVI->audio_index[nai].len; + nai++; + } + } + + AVI->audio_bytes = tot; + + /* Reposition the file */ + + this->input->seek(AVI->movi_start,SEEK_SET); + AVI->video_posf = 0; + AVI->video_posb = 0; + + return AVI; +} + +static long AVI_frame_size(avi_t *AVI, long frame) +{ + if(!AVI->video_index) { AVI->AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(frame < 0 || frame >= AVI->video_frames) return 0; + return(AVI->video_index[frame].len); +} + +static void AVI_seek_start(avi_t *AVI) +{ + AVI->video_posf = 0; + AVI->video_posb = 0; +} + +static int AVI_set_video_position(avi_t *AVI, long frame) +{ + if(!AVI->video_index) { AVI->AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if (frame < 0 ) frame = 0; + AVI->video_posf = frame; + AVI->video_posb = 0; + return 0; +} + +static int AVI_set_audio_position(avi_t *AVI, long byte) +{ + long n0, n1, n; + + if(!AVI->audio_index) { AVI->AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(byte < 0) byte = 0; + + /* Binary search in the audio chunks */ + + n0 = 0; + n1 = AVI->audio_chunks; + + while(n0<n1-1) + { + n = (n0+n1)/2; + if(AVI->audio_index[n].tot>byte) + n1 = n; + else + n0 = n; + } + + AVI->audio_posc = n0; + AVI->audio_posb = byte - AVI->audio_index[n0].tot; + + return 0; +} + +static long AVI_read_audio(demux_avi_t *this, avi_t *AVI, char *audbuf, + long bytes, int *bFrameDone) +{ + long nr, pos, left, todo; + + if(!AVI->audio_index) { AVI->AVI_errno = AVI_ERR_NO_IDX; return -1; } + + nr = 0; /* total number of bytes read */ + + /* printf ("avi audio package len: %d\n", AVI->audio_index[AVI->audio_posc].len); */ + + + while(bytes>0) + { + left = AVI->audio_index[AVI->audio_posc].len - AVI->audio_posb; + if(left==0) + { + AVI->audio_posc++; + AVI->audio_posb = 0; + if (nr>0) { + *bFrameDone = 1; + return nr; + } + left = AVI->audio_index[AVI->audio_posc].len - AVI->audio_posb; + } + if(bytes<left) + todo = bytes; + else + todo = left; + pos = AVI->audio_index[AVI->audio_posc].pos + AVI->audio_posb; + /* printf ("demux_avi: read audio from %d\n", pos); */ + if (this->input->seek (pos, SEEK_SET)<0) + return -1; + if (this->input->read(audbuf+nr,todo) != todo) + { + AVI->AVI_errno = AVI_ERR_READ; + *bFrameDone = 0; + return -1; + } + bytes -= todo; + nr += todo; + AVI->audio_posb += todo; + } + + left = AVI->audio_index[AVI->audio_posc].len - AVI->audio_posb; + *bFrameDone = (left==0); + + return nr; +} + +static long AVI_read_video(demux_avi_t *this, avi_t *AVI, char *vidbuf, + long bytes, int *bFrameDone) +{ + long nr, pos, left, todo; + + if(!AVI->video_index) { AVI->AVI_errno = AVI_ERR_NO_IDX; return -1; } + + nr = 0; /* total number of bytes read */ + + while(bytes>0) + { + left = AVI->video_index[AVI->video_posf].len - AVI->video_posb; + if(left==0) + { + AVI->video_posf++; + AVI->video_posb = 0; + if (nr>0) { + *bFrameDone = 1; + return nr; + } + left = AVI->video_index[AVI->video_posf].len - AVI->video_posb; + } + if(bytes<left) + todo = bytes; + else + todo = left; + pos = AVI->video_index[AVI->video_posf].pos + AVI->video_posb; + /* printf ("demux_avi: read video from %d\n", pos); */ + if (this->input->seek (pos, SEEK_SET)<0) + return -1; + if (this->input->read(vidbuf+nr,todo) != todo) + { + AVI->AVI_errno = AVI_ERR_READ; + *bFrameDone = 0; + return -1; + } + bytes -= todo; + nr += todo; + AVI->video_posb += todo; + } + + left = AVI->video_index[AVI->video_posf].len - AVI->video_posb; + *bFrameDone = (left==0); + + return nr; +} + +static int demux_avi_next (demux_avi_t *this) { + + buf_element_t *buf; + + if (this->avi->video_frames <= this->avi->video_posf) + return 0; + + buf = this->audio_fifo->buffer_pool_alloc (); + + buf->content = buf->mem; + buf->DTS = 0 ; /* FIXME */ + + if (this->avi->audio_index[this->avi->audio_posc].pos< + this->avi->video_index[this->avi->video_posf].pos) { + + /* read audio */ + xprintf (VERBOSE|DEMUX|VAVI, "demux_avi: audio \n"); + /* pBuf->nPTS = (uint32_t) (90000.0 * (this->avi->audio_index[this->avi->audio_posc].tot + this->avi->audio_posb) / this->nAvgBytesPerSec) ; */ + + buf->size = AVI_read_audio (this, this->avi, buf->mem, 2048, &buf->frame_end); + buf->PTS = 0; + buf->input_pos = this->input->seek (0, SEEK_CUR); + + switch (this->avi->a_fmt) { + case 0x01: + buf->type = BUF_AUDIO_LPCM; + break; + case 0x2000: + buf->type = BUF_AUDIO_AC3; + break; + case 0x50: + case 0x55: + buf->type = BUF_AUDIO_MPEG; + break; + case 0x161: + buf->type = BUF_AUDIO_AVI; + break; + default: + printf ("demux_avi: unknown audio type 0x%lx =>exit\n", this->avi->a_fmt); + this->status = DEMUX_FINISHED; + buf->type = BUF_AUDIO_MPEG; + break; + } + + this->audio_fifo->put (this->audio_fifo, buf); + + } else { + /* read video */ + xprintf (VERBOSE|DEMUX|VAVI, "demux_avi: video \n"); + + buf->PTS = 0; + /* buf->nPTS = this->avi->video_posf * this->video_step ; */ + buf->size = AVI_read_video (this, this->avi, buf->mem, 2048, &buf->frame_end); + buf->type = BUF_VIDEO_AVI ; + + this->video_fifo->put (this->video_fifo, buf); + } + + xprintf (VERBOSE|DEMUX|VAVI, "size : %d\n",buf->size); + + return (buf->size>0); +} + +static void *demux_avi_loop (void *this_gen) { + + buf_element_t *buf; + demux_avi_t *this = (demux_avi_t *) this_gen; + + do { + if (!demux_avi_next(this)) + this->status = DEMUX_FINISHED; + + } while (this->status == DEMUX_OK) ; + + buf = this->video_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_END; + this->video_fifo->put (this->video_fifo, buf); + buf = this->audio_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_END; + this->audio_fifo->put (this->audio_fifo, buf); + + xprintf (VERBOSE|DEMUX, "demux_avi: demux loop finished.\n"); + + return NULL; +} + +static void demux_avi_stop (demux_plugin_t *this_gen) { + void *p; + demux_avi_t *this = (demux_avi_t *) this_gen; + + this->status = DEMUX_FINISHED; + + pthread_join (this->thread, &p); + + AVI_close (this->avi); + this->avi = NULL; +} + +static void demux_avi_close (demux_plugin_t *this_gen) { + demux_avi_t *this = (demux_avi_t *) this_gen; + free(this); +} + +static int demux_avi_get_status (demux_plugin_t *this_gen) { + demux_avi_t *this = (demux_avi_t *) this_gen; + return this->status; +} + +static void demux_avi_start (demux_plugin_t *this_gen, + fifo_buffer_t *bufVideo, + fifo_buffer_t *bufAudio, + fifo_buffer_t *bufSPU, + off_t pos) +{ + buf_element_t *buf; + demux_avi_t *this = (demux_avi_t *) this_gen; + + this->audio_fifo = bufVideo; + this->video_fifo = bufAudio; + + this->status = DEMUX_OK; + + this->avi = AVI_init(this); + + if (!this->avi) { + printf ("demux_avi: init failed, avi_errno=%d .\n", this->avi->AVI_errno); + this->status = DEMUX_FINISHED; + return; + } + + printf ("demux_avi: video format = %s, audio format = 0x%lx\n", + this->avi->compressor, this->avi->a_fmt); + + AVI_seek_start (this->avi); + + /* + * seek + */ + + /* seek audio */ + while (this->avi->audio_index[this->avi->audio_posc].pos < pos) { + this->avi->audio_posc++; + if (this->avi->audio_posc>this->avi->audio_chunks) { + this->status = DEMUX_FINISHED; + return; + } + } + + /* seek video */ + + /* + while (this->avi->video_index[this->avi->video_posf].pos < pos) { + this->avi->video_posf++; + if (this->avi->video_posf>this->avi->video_frames) { + this->mnStatus = DEMUX_FINISHED; + return; + } + } + */ + + + this->avi->video_posf = (long) (((double) this->avi->audio_index[this->avi->audio_posc].tot / (double) this->avi->audio_bytes) * (double) this->avi->video_frames); + + + /* + * send start buffers + */ + + buf = this->video_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_START; + this->video_fifo->put (this->video_fifo, buf); + + buf = this->audio_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_START; + this->audio_fifo->put (this->audio_fifo, buf); + + buf = this->video_fifo->buffer_pool_alloc (); + buf->content = buf->mem; + this->avi->bih.biSize = this->video_step; /* HACK */ + memcpy (buf->content, &this->avi->bih, sizeof (this->avi->bih)); + buf->size = sizeof (this->avi->bih); + buf->type = BUF_VIDEO_AVI; + this->video_fifo->put (this->video_fifo, buf); + + buf = this->audio_fifo->buffer_pool_alloc (); + buf->content = buf->mem; + memcpy (buf->content, &this->avi->wavex, + sizeof (this->avi->wavex)); + buf->size = sizeof (this->avi->wavex); + buf->type = BUF_AUDIO_AVI; + this->audio_fifo->put (this->audio_fifo, buf); + + pthread_create (&this->thread, NULL, demux_avi_loop, this) ; +} + +static int demux_avi_open(demux_plugin_t *this_gen, input_plugin_t *input, int stage) { + + demux_avi_t *this = (demux_avi_t *) this_gen; + + switch(stage) { + + case STAGE_BY_CONTENT: { + uint8_t buf[4096]; + + if (input->get_blocksize()) + return DEMUX_CANNOT_HANDLE; + + if (!(input->get_capabilities() & INPUT_CAP_SEEKABLE)) + return DEMUX_CANNOT_HANDLE; + + input->seek(0, SEEK_SET); + + if(input->read(buf, 4)) { + + if((buf[0] == 0x52) + && (buf[1] == 0x49) + && (buf[2] == 0x46) + && (buf[3] == 0x46)) { + this->input = input; + this->avi = AVI_init (this); + if (this->avi) + return DEMUX_CAN_HANDLE; + else { + printf ("demux_avi: AVI_init failed.\n"); + return DEMUX_CANNOT_HANDLE; + } + } + + } + return DEMUX_CANNOT_HANDLE; + } + break; + + case STAGE_BY_EXTENSION: { + char *ending, *mrl; + + mrl = this->input->get_mrl (); + + ending = strrchr(mrl, '.'); + xprintf(VERBOSE|DEMUX, "demux_avi_can_handle: ending %s of %s\n", + ending, mrl); + + if(ending) { + if(!strcasecmp(ending, ".avi")) { + this->input = input; + this->avi = AVI_init (this); + if (this->avi) + return DEMUX_CAN_HANDLE; + else { + printf ("demux_avi: AVI_init failed.\n"); + return DEMUX_CANNOT_HANDLE; + } + } + } + + return DEMUX_CANNOT_HANDLE; + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +static char *demux_avi_get_id(demux_plugin_t *this) { + return "AVI"; +} + +demux_plugin_t *init_demux_avi(config_values_t *cfg, uint32_t xd) { + + demux_avi_t *this = xmalloc (sizeof (demux_avi_t)); + + xine_debug = xd; + + this->demux_plugin.open = demux_avi_open; + this->demux_plugin.start = demux_avi_start; + this->demux_plugin.stop = demux_avi_stop; + this->demux_plugin.close = demux_avi_close; + this->demux_plugin.get_status = demux_avi_get_status; + this->demux_plugin.get_identifier = demux_avi_get_id; + + + return (demux_plugin_t *) this; +} diff --git a/src/demuxers/demux_elem.c b/src/demuxers/demux_elem.c new file mode 100644 index 000000000..855c22e17 --- /dev/null +++ b/src/demuxers/demux_elem.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix 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: demux_elem.c,v 1.1 2001/04/18 22:33:58 f1rmb Exp $ + * + * demultiplexer for elementary mpeg streams + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> + +#include "xine.h" +#include "monitor.h" +#include "demux.h" + +static uint32_t xine_debug; + +typedef struct _demux_mpeg_elem_globals { + fifo_buffer_t *mBufVideo; + fifo_buffer_t *mBufAudio; + + input_plugin_t *mInput; + pthread_t mThread; + int mnBlocksize; + + int mnStatus; +} demux_mpeg_elem_globals_t ; + +static demux_mpeg_elem_globals_t gDemuxMpegElem; +static fifobuf_functions_t *Ffb; + +/* + * + */ +static int demux_mpeg_elem_next (void) { + + buf_element_t *pBuf; + + pBuf = Ffb->buffer_pool_alloc (); + + pBuf->pContent = pBuf->pMem; + pBuf->nDTS = 0; + pBuf->nPTS = 0; + pBuf->nSize = gDemuxMpegElem.mInput->read(pBuf->pMem, + gDemuxMpegElem.mnBlocksize); + pBuf->nType = BUF_MPEGELEMENT; + pBuf->nInputPos = gDemuxMpegElem.mInput->seek (0, SEEK_CUR); + + Ffb->fifo_buffer_put (gDemuxMpegElem.mBufVideo, pBuf); + + return (pBuf->nSize==gDemuxMpegElem.mnBlocksize); +} + +/* + * + */ +static void *demux_mpeg_elem_loop (void *dummy) { + buf_element_t *pBuf; + + do { + + if (!demux_mpeg_elem_next()) + gDemuxMpegElem.mnStatus = DEMUX_FINISHED; + + } while (gDemuxMpegElem.mnStatus == DEMUX_OK) ; + + xprintf (VERBOSE|DEMUX, "demux loop finished (status: %d)\n", + gDemuxMpegElem.mnStatus); + + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_STREAMEND; + Ffb->fifo_buffer_put (gDemuxMpegElem.mBufVideo, pBuf); + + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_STREAMEND; + Ffb->fifo_buffer_put (gDemuxMpegElem.mBufAudio, pBuf); + + return NULL; +} + +/* + * + */ +static void demux_mpeg_elem_stop (void) { + void *p; + + gDemuxMpegElem.mnStatus = DEMUX_FINISHED; + + Ffb->fifo_buffer_clear(gDemuxMpegElem.mBufVideo); + Ffb->fifo_buffer_clear(gDemuxMpegElem.mBufAudio); + + pthread_join (gDemuxMpegElem.mThread, &p); +} + +/* + * + */ +static int demux_mpeg_elem_get_status (void) { + return gDemuxMpegElem.mnStatus; +} + +/* + * + */ +static void demux_mpeg_elem_start (input_plugin_t *input_plugin, + fifo_buffer_t *bufVideo, + fifo_buffer_t *bufAudio, + fifo_buffer_t *bufSPU, + off_t pos) +{ + buf_element_t *pBuf; + + gDemuxMpegElem.mInput = input_plugin; + gDemuxMpegElem.mBufVideo = bufVideo; + gDemuxMpegElem.mBufAudio = bufAudio; + + gDemuxMpegElem.mnStatus = DEMUX_OK; + /* + if ((gDemuxMpegElem.mInput->get_capabilities() & INPUT_CAP_SEEKABLE) != 0 ) { + xprintf (VERBOSE|DEMUX, "=>seek to %Ld\n",pos); + + gDemuxMpegElem.mInput->seek (pos, SEEK_SET); + } + else { */ + if((gDemuxMpegElem.mInput->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) + gDemuxMpegElem.mInput->seek (pos, SEEK_SET); +/* } */ + + gDemuxMpegElem.mnBlocksize = 2048; + // pos /= (off_t) gDemuxMpegElem.mnBlocksize; + // pos *= (off_t) gDemuxMpegElem.mnBlocksize; + // xprintf (VERBOSE|DEMUX, "=>seek to %Ld\n",pos); + + // gDemuxMpegElem.mInput->seek (pos, SEEK_SET); + + /* + * send reset buffer + */ + + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_RESET; + Ffb->fifo_buffer_put (gDemuxMpegElem.mBufVideo, pBuf); + + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_RESET; + Ffb->fifo_buffer_put (gDemuxMpegElem.mBufAudio, pBuf); + + /* + * now start demuxing + */ + + pthread_create (&gDemuxMpegElem.mThread, NULL, demux_mpeg_elem_loop, NULL) ; +} + +/* + * + */ +static void demux_mpeg_elem_select_audio_channel (int nChannel) { +} + +/* + * + */ +static void demux_mpeg_elem_select_spu_channel (int nChannel) { +} + +/* + * + */ +static int demux_mpeg_elem_open(input_plugin_t *ip, + const char *MRL, int stage) { + + switch(stage) { + + case STAGE_BY_CONTENT: { + uint8_t buf[4096]; + int bs = 0; + + if(!ip) + return DEMUX_CANNOT_HANDLE; + + if((ip->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) { + ip->seek(0, SEEK_SET); + + if(ip->get_blocksize) + bs = ip->get_blocksize(); + + bs = (bs > 4) ? bs : 4; + + if(ip->read(buf, bs)) { + + if(buf[0] || buf[1] || (buf[2] != 0x01)) + return DEMUX_CANNOT_HANDLE; + + switch(buf[3]) { + case 0xb3: + return DEMUX_CAN_HANDLE; + break; + } + } + } + return DEMUX_CANNOT_HANDLE; + } + break; + + case STAGE_BY_EXTENSION: { + char *suffix; + + suffix = strrchr(MRL, '.'); + xprintf(VERBOSE|DEMUX, "demux_pure_can_handle: suffix %s of %s\n", + suffix, MRL); + + if(suffix) { + if(!strcasecmp(suffix, ".mpv")) + return DEMUX_CAN_HANDLE; + } + + return DEMUX_CANNOT_HANDLE; + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +/* + * + */ +static char *demux_mpeg_elem_get_id(void) { + return "MPEG_ELEM"; +} + +/* + * + */ +static demux_functions_t demux_mpeg_elem_functions = { + NULL, + NULL, + demux_mpeg_elem_open, + demux_mpeg_elem_start, + demux_mpeg_elem_stop, + demux_mpeg_elem_get_status, + demux_mpeg_elem_select_audio_channel, + demux_mpeg_elem_select_spu_channel, + demux_mpeg_elem_get_id +}; + +/* + * + */ +demux_functions_t *init_demux_mpeg_elem(fifobuf_functions_t *f, uint32_t xd) { + + Ffb = f; + xine_debug = xd; + return &demux_mpeg_elem_functions; +} diff --git a/src/demuxers/demux_mpeg.c b/src/demuxers/demux_mpeg.c new file mode 100644 index 000000000..9fcb37df4 --- /dev/null +++ b/src/demuxers/demux_mpeg.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix 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: demux_mpeg.c,v 1.1 2001/04/18 22:33:58 f1rmb Exp $ + * + * demultiplexer for mpeg 1/2 program streams + * reads streams of variable blocksizes + * + * currently only used for mpeg-1-files + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> + +#include "monitor.h" +#include "xine.h" +#include "demux.h" +#include "utils.h" + +static uint32_t xine_debug; + +typedef struct demux_mpeg_s { + + demux_plugin_t demux_plugin; + + fifo_buffer_t *audio_fifo; + fifo_buffer_t *video_fifo; + + input_plugin_t *input; + + pthread_t thread; + + unsigned char dummy_space[100000]; + + int status; +} demux_mpeg_t ; + +static uint32_t read_bytes (demux_mpeg_t *this, int n) { + + uint32_t res; + uint32_t i; + unsigned char buf[6]; + + buf[4]=0; + + i = this->input->read (buf, n); + + if (i != n) { + this->status = DEMUX_FINISHED; + xprintf (VERBOSE|DEMUX, "Unexpected end of stream\n"); + } + + + switch (n) { + case 1: + res = buf[0]; + break; + case 2: + res = (buf[0]<<8) | buf[1]; + break; + case 3: + res = (buf[0]<<16) | (buf[1]<<8) | buf[2]; + break; + case 4: + res = (buf[2]<<8) | buf[3] | (buf[1]<<16) | (buf[0] << 24); + break; + default: + fprintf (stderr, + "How how - something wrong in wonderland demux:read_bytes (%d)\n", + n); + exit (1); + } + + return res; +} + +static void parse_mpeg2_packet (demux_mpeg_t *this, int nID) { + + int nLen, i; + uint32_t w, flags, header_len, pts; + buf_element_t *buf; + + nLen = read_bytes(this, 2); + + xprintf (VERBOSE|DEMUX|MPEG, " mpeg2 packet (len=%d",nLen); + + if (nID==0xbd) { + + int track; + + xprintf (VERBOSE|DEMUX|AC3, ",ac3"); + + w = read_bytes(this, 1); + flags = read_bytes(this, 1); + header_len = read_bytes(this, 1); + + nLen -= header_len + 3; + + pts=0; + + if ((flags & 0x80) == 0x80) { + + w = read_bytes(this, 1); + pts = (w & 0x0e) << 29 ; + w = read_bytes(this, 2); + pts |= (w & 0xFFFE) << 14; + w = read_bytes(this, 2); + pts |= (w & 0xFFFE) >> 1; + + xprintf (VERBOSE|DEMUX|VPTS, ", pts=%d",pts); + + header_len -= 5 ; + } + + /* read rest of header */ + i = this->input->read (this->dummy_space, header_len+4); + + track = this->dummy_space[0] & 0x0F ; + + xprintf (VERBOSE|DEMUX, ", track=%02x", track); + + /* contents */ + + buf = this->input->read_block (this->audio_fifo, nLen-4); + + buf->type = BUF_AUDIO_AC3 + track; + buf->PTS = pts; + buf->DTS = 0 ; /* FIXME */ + buf->input_pos = this->input->get_current_pos (); + + this->audio_fifo->put (this->audio_fifo, buf); + + } else if ((nID & 0xe0) == 0xc0) { + int track = nID & 0x1f; + + xprintf (VERBOSE|DEMUX|AUDIO, ", audio #%d", track); + + w = read_bytes(this, 1); + flags = read_bytes(this, 1); + header_len = read_bytes(this, 1); + + nLen -= header_len + 3; + + pts = 0; + + if ((flags & 0x80) == 0x80) { + + w = read_bytes(this, 1); + pts = (w & 0x0e) << 29 ; + w = read_bytes(this, 2); + pts |= (w & 0xFFFE) << 14; + w = read_bytes(this, 2); + pts |= (w & 0xFFFE) >> 1; + + xprintf (VERBOSE|DEMUX|VPTS, ", pts=%d",pts); + + header_len -= 5 ; + } + + /* read rest of header */ + i = this->input->read (this->dummy_space, header_len); + + buf = this->input->read_block (this->audio_fifo, nLen); + + buf->type = BUF_AUDIO_MPEG + track; + buf->PTS = pts; + buf->DTS = 0; /* FIXME */ + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->audio_fifo->put (this->audio_fifo, buf); + + } else if ((nID >= 0xbc) && ((nID & 0xf0) == 0xe0)) { + + xprintf (VERBOSE|DEMUX|VIDEO, ",video"); + + w = read_bytes(this, 1); + flags = read_bytes(this, 1); + header_len = read_bytes(this, 1); + + nLen -= header_len + 3; + + pts = 0; + + if ((flags & 0x80) == 0x80) { + + w = read_bytes(this, 1); + pts = (w & 0x0e) << 29 ; + w = read_bytes(this, 2); + pts |= (w & 0xFFFE) << 14; + w = read_bytes(this, 2); + pts |= (w & 0xFFFE) >> 1; + + xprintf (VERBOSE|DEMUX|VPTS, ", pts=%d",pts); + + header_len -= 5 ; + } + + /* read rest of header */ + i = this->input->read (this->dummy_space, header_len); + + /* contents */ + + buf = this->input->read_block (this->audio_fifo, nLen); + + buf->type = BUF_VIDEO_MPEG; + buf->PTS = pts; + buf->DTS = 0; + + this->video_fifo->put (this->video_fifo, buf); + + } else { + xprintf (VERBOSE|DEMUX, ",unknown stream - skipped"); + + i = this->input->read (this->dummy_space, nLen); + /* (*this->input->seek) (nLen,SEEK_CUR); */ + } + + xprintf (VERBOSE|DEMUX, ")\n"); + +} + +static void parse_mpeg1_packet (demux_mpeg_t *this, int nID) +{ + int nLen; + uint32_t w; + int i; + int pts; + buf_element_t *buf; + + xprintf (VERBOSE|DEMUX, " packet ("); + + nLen = read_bytes(this, 2); + + xprintf (VERBOSE|DEMUX, "len=%d",nLen); + + pts=0; + + if (nID != 0xbf) { + + w = read_bytes(this, 1); nLen--; + + while ((w & 0x80) == 0x80) { + + if (this->status != DEMUX_OK) + return; + + /* stuffing bytes */ + w = read_bytes(this, 1); nLen--; + } + + if ((w & 0xC0) == 0x40) { + + if (this->status != DEMUX_OK) + return; + + /* buffer_scale, buffer size */ + w = read_bytes(this, 1); nLen--; + w = read_bytes(this, 1); nLen--; + } + + if ((w & 0xF0) == 0x20) { + + if (this->status != DEMUX_OK) + return; + + pts = (w & 0xe) << 29 ; + w = read_bytes(this, 2); nLen -= 2; + + pts |= (w & 0xFFFE) << 14; + + w = read_bytes(this, 2); nLen -= 2; + pts |= (w & 0xFFFE) >> 1; + + xprintf (VERBOSE|DEMUX|VPTS, ", pts=%d",pts); + + /* pts = 0; */ + + } else if ((w & 0xF0) == 0x30) { + + if (this->status != DEMUX_OK) + return; + + pts = (w & 0x0e) << 29 ; + w = read_bytes(this, 2); nLen -= 2; + + pts |= (w & 0xFFFE) << 14; + + w = read_bytes(this, 2); nLen -= 2; + + pts |= (w & 0xFFFE) >> 1; + +/* printf ("pts2=%d\n",pts); */ + xprintf (VERBOSE|DEMUX|VPTS, ", pts2=%d",pts); + + /* Decoding Time Stamp */ + w = read_bytes(this, 3); nLen -= 3; + w = read_bytes(this, 2); nLen -= 2; + } else { + xprintf (VERBOSE|DEMUX, ", w = %02x",w); + if (w != 0x0f) + xprintf (VERBOSE|DEMUX, " ERROR w (%02x) != 0x0F ",w); + } + + } + + if ((nID & 0xe0) == 0xc0) { + int track = nID & 0x1f; + + xprintf (VERBOSE|DEMUX|AUDIO, ", audio #%d", track); + + buf = this->input->read_block (this->audio_fifo, nLen); + + buf->type = BUF_AUDIO_MPEG + track ; + buf->PTS = pts; + buf->DTS = 0; /* FIXME */ + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->audio_fifo->put (this->audio_fifo, buf); + + } else if ((nID & 0xf0) == 0xe0) { + + xprintf (VERBOSE|DEMUX|VIDEO, ", video #%d", nID & 0x0f); + + buf = this->input->read_block (this->video_fifo, nLen); + + buf->type = BUF_VIDEO_MPEG; + buf->PTS = pts; + buf->DTS = 0; /* FIXME */ + + this->video_fifo->put (this->video_fifo, buf); + + } else if (nID == 0xbd) { + xprintf (VERBOSE|DEMUX|AC3, ", ac3"); + i = this->input->read (this->dummy_space, nLen); + } else { + xprintf (VERBOSE|DEMUX, ", unknown (nID = %d)",nID); + this->input->read (this->dummy_space, nLen); + } + + xprintf (VERBOSE|DEMUX, ")\n"); +} + +static uint32_t parse_pack(demux_mpeg_t *this) +{ + uint32_t buf ; + char scratch[1024]; + int mpeg_version; + + xprintf (VERBOSE|DEMUX, "pack {\n"); + + /* system_clock_reference */ + buf = read_bytes (this, 1); + xprintf (VERBOSE|DEMUX|VIDEO, " mpeg version : %02x",buf>>4); + + if ((buf>>4) == 4) { + xprintf (VERBOSE|DEMUX|VIDEO, " => mpeg II \n"); + buf = read_bytes(this, 2); + mpeg_version = 2; + } else { + xprintf (VERBOSE|DEMUX|VIDEO, " => mpeg I \n"); + mpeg_version = 1; + } + + buf = read_bytes (this, 2); + buf = read_bytes (this, 2); + + /* mux_rate */ + + buf = read_bytes (this, 3) ; + + /* printf (" mux_rate = %06x\n",buf); */ + + /* system header */ + + buf = read_bytes (this, 4) ; + + /* printf (" code = %08x\n",buf);*/ + + if (buf == 0x000001bb) { + buf = read_bytes (this, 2); + xprintf (VERBOSE|DEMUX, " system_header (%d +6 bytes)\n",buf); + + this->input->read (scratch,buf); + + buf = read_bytes (this, 4) ; + } + + /* printf (" code = %08x\n",buf); */ + + while ( ((buf & 0xFFFFFF00) == 0x00000100) + && ((buf & 0xff) != 0xba) ) { + + if (this->status != DEMUX_OK) + return buf; + + if (mpeg_version == 1) + parse_mpeg1_packet (this, buf & 0xFF); + else + parse_mpeg2_packet (this, buf & 0xFF); + + buf = read_bytes (this, 4); + xprintf (VERBOSE|DEMUX, " code = %08x\n",buf); + } + + xprintf (VERBOSE|DEMUX, "}\n"); + + return buf; + +} + +static void demux_mpeg_resync (demux_mpeg_t *this, uint32_t buf) { + + while ((buf !=0x000001ba) && (this->status == DEMUX_OK)) { + xprintf (VERBOSE|DEMUX, "resync : %08x\n",buf); + buf = (buf << 8) | read_bytes (this, 1); + } +} + +static void *demux_mpeg_loop (void *this_gen) { + + demux_mpeg_t *this = (demux_mpeg_t *) this_gen; + buf_element_t *buf; + uint32_t w; + + do { + w = parse_pack (this); + + if (w != 0x000001ba) + demux_mpeg_resync (this, w); + + } while (this->status == DEMUX_OK) ; + + buf = this->video_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_END; + this->video_fifo->put (this->video_fifo, buf); + buf = this->audio_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_END; + this->audio_fifo->put (this->audio_fifo, buf); + + xprintf (VERBOSE|DEMUX, "demux loop finished (status: %d, buf:%x)\n", + this->status, w); + + return NULL; +} + +static void demux_mpeg_stop (demux_plugin_t *this_gen) { + void *p; + demux_mpeg_t *this = (demux_mpeg_t *) this_gen; + + this->status = DEMUX_FINISHED; + + pthread_join (this->thread, &p); +} + +static int demux_mpeg_get_status (demux_plugin_t *this_gen) { + demux_mpeg_t *this = (demux_mpeg_t *) this_gen; + return this->status; +} + +static void demux_mpeg_start (demux_plugin_t *this_gen, + fifo_buffer_t *video_fifo, + fifo_buffer_t *audio_fifo, + fifo_buffer_t *spu_fifo, + off_t pos) +{ + demux_mpeg_t *this = (demux_mpeg_t *) this_gen; + buf_element_t *buf; + + this->video_fifo = video_fifo; + this->audio_fifo = audio_fifo; + + this->status = DEMUX_OK; + + if ((this->input->get_capabilities () & INPUT_CAP_SEEKABLE) != 0 ) { + xprintf (VERBOSE|DEMUX, "=>seek to %Ld\n",pos); + this->input->seek (pos+4, SEEK_SET); + } + + buf = this->video_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_START; + this->video_fifo->put (this->video_fifo, buf); + buf = this->audio_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_START; + this->audio_fifo->put (this->audio_fifo, buf); + + pthread_create (&this->thread, NULL, demux_mpeg_loop, this) ; +} + +static int demux_mpeg_open(demux_plugin_t *this_gen, input_plugin_t *ip, int stage) { + + demux_mpeg_t *this = (demux_mpeg_t *) this_gen; + + this->input = ip; + + switch(stage) { + + case STAGE_BY_CONTENT: { + uint8_t buf[4096]; + + if((ip->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) { + ip->seek(0, SEEK_SET); + + if(ip->get_blocksize()) + return DEMUX_CANNOT_HANDLE; + + if(ip->read(buf, 6)) { + + if(buf[0] || buf[1] || (buf[2] != 0x01)) + return DEMUX_CANNOT_HANDLE; + + switch(buf[3]) { + + case 0xba: + if((buf[4] & 0xf0) == 0x20) + return DEMUX_CAN_HANDLE; + break; + + case 0xe0: + if((buf[6] & 0xc0) != 0x80) + return DEMUX_CAN_HANDLE; + break; + + } + } + } + return DEMUX_CANNOT_HANDLE; + } + break; + + case STAGE_BY_EXTENSION: { + char *media; + char *ending; + char *MRL = ip->get_mrl(); + + media = strstr(MRL, "://"); + if(media) { + if((!(strncasecmp(MRL, "stdin", 5))) + || (!(strncasecmp(MRL, "fifo", 4)))) { + if(!(strncasecmp((media+3), "mpeg1", 5))) { + perr("%s(%d)mpeg\n", __FUNCTION__, stage); + return DEMUX_CAN_HANDLE; + } + else if(!(strncasecmp((media+3), "mpeg2", 5))) { + return DEMUX_CANNOT_HANDLE; + } + fprintf(stderr, "You should specify mpeg(mpeg1/mpeg2) stream type.\n"); + return DEMUX_CANNOT_HANDLE; + } + else if(strncasecmp(MRL, "file", 4)) { + return DEMUX_CANNOT_HANDLE; + } + } + + ending = strrchr(MRL, '.'); + xprintf(VERBOSE|DEMUX, "demux_mpeg_can_handle: ending %s of %s\n", + ending, MRL); + + if(!ending) + return DEMUX_CANNOT_HANDLE; + + if(!strcasecmp(ending, ".mpg") + || (!strcasecmp(ending, ".mpeg"))) { + return DEMUX_CAN_HANDLE; + } + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +static void demux_mpeg_select_spu_channel (int nChannel) { +} + +static char *demux_mpeg_get_id(demux_plugin_t *this) { + return "MPEG"; +} + +static void demux_mpeg_close (demux_plugin_t *this) { + /* nothing */ +} + +demux_plugin_t *init_demux_mpeg(config_values_t *cfg, uint32_t xd) { + + demux_mpeg_t *this = xmalloc (sizeof (demux_mpeg_t)); + + xine_debug = xd; + + this->demux_plugin.open = demux_mpeg_open; + this->demux_plugin.start = demux_mpeg_start; + this->demux_plugin.stop = demux_mpeg_stop; + this->demux_plugin.close = demux_mpeg_close; + this->demux_plugin.get_status = demux_mpeg_get_status; + this->demux_plugin.get_identifier = demux_mpeg_get_id; + + return (demux_plugin_t *) this; +} + diff --git a/src/demuxers/demux_mpeg_block.c b/src/demuxers/demux_mpeg_block.c new file mode 100644 index 000000000..531bf44aa --- /dev/null +++ b/src/demuxers/demux_mpeg_block.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix 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: demux_mpeg_block.c,v 1.1 2001/04/18 22:33:58 f1rmb Exp $ + * + * demultiplexer for mpeg 1/2 program streams + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> + +#include "xine.h" +#include "monitor.h" +#include "demux.h" +#include "utils.h" + +static uint32_t xine_debug; + +typedef struct demux_mpeg_block_s { + demux_plugin_t demux_plugin; + + fifo_buffer_t *audio_fifo; + fifo_buffer_t *video_fifo; + fifo_buffer_t *spu_fifo; + + input_plugin_t *input; + + pthread_t thread; + + int status; + + int blocksize; +} demux_mpeg_block_t ; + + +static void demux_mpeg_block_parse_pack (demux_mpeg_block_t *this) { + + buf_element_t *buf; + unsigned char *p; + int bMpeg1=0; + uint32_t nHeaderLen; + uint32_t nPTS; + uint32_t nDTS; + uint32_t nPacketLen; + uint32_t nStreamID; + + + buf = this->input->read_block (this->video_fifo, this->blocksize); + + if (buf==NULL) { + this->status = DEMUX_FINISHED; + return ; + } + + p = buf->content; /* len = this->mnBlocksize; */ + + if (p[3] == 0xBA) { /* program stream pack header */ + + int nStuffingBytes; + + xprintf (VERBOSE|DEMUX, "program stream pack header\n"); + + bMpeg1 = (p[4] & 0x40) == 0; + + if (bMpeg1) { + + p += 12; + + } else { /* mpeg2 */ + + nStuffingBytes = p[0xD] & 0x07; + + xprintf (VERBOSE|DEMUX, "%d stuffing bytes\n",nStuffingBytes); + + p += 14 + nStuffingBytes; + } + } + + + if (p[3] == 0xbb) { /* program stream system header */ + + int nHeaderLen; + + xprintf (VERBOSE|DEMUX, "program stream system header\n"); + + nHeaderLen = (p[4] << 8) | p[5]; + + p += 6 + nHeaderLen; + } + + /* we should now have a PES packet here */ + + if (p[0] || p[1] || (p[2] != 1)) { + fprintf (stderr, "demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]); + buf->free_buffer (buf); + return ; + } + + nPacketLen = p[4] << 8 | p[5]; + nStreamID = p[3]; + + xprintf (VERBOSE|DEMUX, "packet id = %02x len = %d\n",nStreamID, nPacketLen); + + if (bMpeg1) { + + if (nStreamID == 0xBF) { + buf->free_buffer (buf); + return ; + } + + p += 6; /* nPacketLen -= 6; */ + + while ((p[0] & 0x80) == 0x80) { + p++; + nPacketLen--; + /* printf ("stuffing\n");*/ + } + + if ((p[0] & 0xc0) == 0x40) { + /* STD_buffer_scale, STD_buffer_size */ + p += 2; + nPacketLen -=2; + } + + nPTS = 0; + nDTS = 0; + if ((p[0] & 0xf0) == 0x20) { + nPTS = (p[ 0] & 0x0E) << 29 ; + nPTS |= p[ 1] << 22 ; + nPTS |= (p[ 2] & 0xFE) << 14 ; + nPTS |= p[ 3] << 7 ; + nPTS |= (p[ 4] & 0xFE) >> 1 ; + p += 5; + nPacketLen -=5; + } else if ((p[0] & 0xf0) == 0x30) { + nPTS = (p[ 0] & 0x0E) << 29 ; + nPTS |= p[ 1] << 22 ; + nPTS |= (p[ 2] & 0xFE) << 14 ; + nPTS |= p[ 3] << 7 ; + nPTS |= (p[ 4] & 0xFE) >> 1 ; + nDTS = (p[ 5] & 0x0E) << 29 ; + nDTS |= p[ 6] << 22 ; + nDTS |= (p[ 7] & 0xFE) << 14 ; + nDTS |= p[ 8] << 7 ; + nDTS |= (p[ 9] & 0xFE) >> 1 ; + p += 10; + nPacketLen -= 10; + } else { + p++; + nPacketLen --; + } + + } else { /* mpeg 2 */ + + if (p[7] & 0x80) { /* PTS avail */ + + nPTS = (p[ 9] & 0x0E) << 29 ; + nPTS |= p[10] << 22 ; + nPTS |= (p[11] & 0xFE) << 14 ; + nPTS |= p[12] << 7 ; + nPTS |= (p[13] & 0xFE) >> 1 ; + + } else + nPTS = 0; + + if (p[7] & 0x40) { /* PTS avail */ + + nDTS = (p[14] & 0x0E) << 29 ; + nDTS |= p[15] << 22 ; + nDTS |= (p[16] & 0xFE) << 14 ; + nDTS |= p[17] << 7 ; + nDTS |= (p[18] & 0xFE) >> 1 ; + + } else + nDTS = 0; + + + nHeaderLen = p[8]; + + p += nHeaderLen + 9; + nPacketLen -= nHeaderLen + 3; + } + + xprintf (VERBOSE|DEMUX, "stream_id=%x len=%d pts=%d dts=%d\n", nStreamID, nPacketLen, nPTS, nDTS); + + if (nStreamID == 0xbd) { + + int nTrack, nSPUID; + + nTrack = p[0] & 0x0F; /* hack : ac3 track */ + + if((p[0] & 0xE0) == 0x20) { + nSPUID = (p[0] & 0x1f); + + xprintf(VERBOSE|DEMUX, "SPU PES packet, id 0x%03x\n",p[0] & 0x1f); + + buf->content = p+1; + buf->size = nPacketLen-1; + buf->type = BUF_SPU_PACKAGE + nSPUID; + buf->PTS = nPTS; + buf->DTS = nDTS ; + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->spu_fifo->put (this->spu_fifo, buf); + + return; + } + + if ((p[0]&0xF0) == 0x80) { + + xprintf (VERBOSE|DEMUX|AC3, "ac3 PES packet, track %02x\n",nTrack); + /* printf ( "ac3 PES packet, track %02x\n",nTrack); */ + + buf->content = p+4; + buf->size = nPacketLen-4; + buf->type = BUF_AUDIO_AC3 + nTrack; + buf->PTS = nPTS; + buf->DTS = nDTS ; + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->audio_fifo->put (this->audio_fifo, buf); + + return ; + } else if ((p[0]&0xf0) == 0xa0) { + + int pcm_offset; + + xprintf (VERBOSE|DEMUX,"LPCMacket, len : %d %02x\n",nPacketLen-4, p[0]); + + for( pcm_offset=0; ++pcm_offset < nPacketLen-1 ; ){ + if ( p[pcm_offset] == 0x01 && p[pcm_offset+1] == 0x80 ) { /* START */ + pcm_offset += 2; + break; + } + } + + buf->content = p+pcm_offset; + buf->size = nPacketLen-pcm_offset; + buf->type = BUF_AUDIO_LPCM + nTrack; + buf->PTS = nPTS; + buf->DTS = nDTS ; + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->audio_fifo->put (this->audio_fifo, buf); + + return ; + } + + } else if ((nStreamID >= 0xbc) && ((nStreamID & 0xf0) == 0xe0)) { + + xprintf (VERBOSE|DEMUX, "video %d\n", nStreamID); + + buf->content = p; + buf->size = nPacketLen; + buf->type = BUF_VIDEO_MPEG; + buf->PTS = nPTS; + buf->DTS = nDTS; + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->video_fifo->put (this->video_fifo, buf); + + return ; + + } else if ((nStreamID & 0xe0) == 0xc0) { + int nTrack; + + nTrack = nStreamID & 0x1f; + + xprintf (VERBOSE|DEMUX|MPEG, "mpg audio #%d", nTrack); + + buf->content = p; + buf->size = nPacketLen; + buf->type = BUF_AUDIO_MPEG + nTrack; + buf->PTS = nPTS; + buf->DTS = nDTS; + buf->input_pos = this->input->seek (0, SEEK_CUR); + + this->audio_fifo->put (this->audio_fifo, buf); + + return ; + + } else { + xprintf (VERBOSE | DEMUX, "unknown packet, id = %x\n",nStreamID); + } + + buf->free_buffer (buf); + + return ; + +} + +static void *demux_mpeg_block_loop (void *this_gen) { + + buf_element_t *buf; + demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen; + + do { + + demux_mpeg_block_parse_pack(this); + + } while (this->status == DEMUX_OK) ; + + xprintf (VERBOSE|DEMUX, "demux loop finished (status: %d)\n", + this->mnStatus); + + this->status = DEMUX_FINISHED; + + buf = this->video_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_END; + this->video_fifo->put (this->video_fifo, buf); + + buf = this->audio_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_END; + this->audio_fifo->put (this->audio_fifo, buf); + + return NULL; +} + +static void demux_mpeg_block_stop (demux_plugin_t *this_gen) { + + demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen; + void *p; + + this->status = DEMUX_FINISHED; + + pthread_join (this->thread, &p); +} + +static int demux_mpeg_block_get_status (demux_plugin_t *this_gen) { + demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen; + + return this->status; +} + +static void demux_mpeg_block_start (demux_plugin_t *this_gen, + fifo_buffer_t *video_fifo, + fifo_buffer_t *audio_fifo, + fifo_buffer_t *spu_fifo, + off_t pos) +{ + + demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen; + buf_element_t *buf; + + this->video_fifo = video_fifo; + this->audio_fifo = audio_fifo; + this->spu_fifo = spu_fifo; + + this->status = DEMUX_OK; + + pos /= (off_t) this->blocksize; + pos *= (off_t) this->blocksize; + + if((this->input->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) { + xprintf (VERBOSE|DEMUX, "=>seek to %Ld\n",pos); + this->input->seek (pos, SEEK_SET); + } + + /* + * send start buffer + */ + + buf = this->video_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_START; + this->video_fifo->put (this->video_fifo, buf); + buf = this->audio_fifo->buffer_pool_alloc (); + buf->type = BUF_CONTROL_START; + this->audio_fifo->put (this->audio_fifo, buf); + + /* + * now start demuxing + */ + + pthread_create (&this->thread, NULL, demux_mpeg_block_loop, this) ; +} + +static int demux_mpeg_block_open(demux_plugin_t *this_gen, + input_plugin_t *input, int stage) { + + demux_mpeg_block_t *this = (demux_mpeg_block_t *) this_gen; + + this->input = input; + + switch(stage) { + + case STAGE_BY_CONTENT: { + uint8_t buf[4096]; + + if((input->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) { + input->seek(0, SEEK_SET); + + this->blocksize = input->get_blocksize(); + + if (!this->blocksize) + return DEMUX_CANNOT_HANDLE; + + if (input->read(buf, this->blocksize)) { + + if(buf[0] || buf[1] || (buf[2] != 0x01)) + return DEMUX_CANNOT_HANDLE; + + switch(buf[3]) { + + case 0xba: + if((buf[4] & 0xc0) == 0x40) + return DEMUX_CAN_HANDLE; + + break; + + case 0xe0: + if((buf[6] & 0xc0) == 0x80) + return DEMUX_CAN_HANDLE; + + break; + + } + } + } + return DEMUX_CANNOT_HANDLE; + } + break; + + case STAGE_BY_EXTENSION: { + char *media; + char *ending; + char *MRL; + + MRL = input->get_mrl (); + + media = strstr(MRL, "://"); + if(media) { + if(!strncmp(MRL, "dvd", 3) + || !strncmp(MRL, "fifo", 4) + || (((!strncmp(MRL, "stdin", 5) || !strncmp(MRL, "fifo", 4)) + && (!strncmp((media+3), "mpeg2", 5) ))) + ) { + this->blocksize = 2048; + return DEMUX_CAN_HANDLE; + } + if(!strncmp(MRL, "vcd", 3)) { + this->blocksize = 2324; + return DEMUX_CAN_HANDLE; + } + } + + /* + * check ending + */ + + ending = strrchr(MRL, '.'); + + xprintf(VERBOSE|DEMUX, "demux_mpeg_block_can_handle: ending %s of %s\n", + ending ? ending :"(none)", MRL); + + if(!ending) + return DEMUX_CANNOT_HANDLE; + + if(!strcasecmp(ending, ".vob")) { + this->blocksize = 2048; + return DEMUX_CAN_HANDLE; + } + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +static char *demux_mpeg_block_get_id(demux_plugin_t *this) { + return "MPEG_BLOCK"; +} + +static void demux_mpeg_block_close (demux_plugin_t *this) { + /* nothing */ +} + +demux_plugin_t *init_demux_mpeg_block(config_values_t *cfg, uint32_t xd) { + + demux_mpeg_block_t *this = xmalloc (sizeof (demux_mpeg_block_t)); + + xine_debug = xd; + + this->demux_plugin.open = demux_mpeg_block_open; + this->demux_plugin.start = demux_mpeg_block_start; + this->demux_plugin.stop = demux_mpeg_block_stop; + this->demux_plugin.close = demux_mpeg_block_close; + this->demux_plugin.get_status = demux_mpeg_block_get_status; + this->demux_plugin.get_identifier = demux_mpeg_block_get_id; + + return (demux_plugin_t *) this; +} diff --git a/src/demuxers/demux_mpgaudio.c b/src/demuxers/demux_mpgaudio.c new file mode 100644 index 000000000..8eea5bddf --- /dev/null +++ b/src/demuxers/demux_mpgaudio.c @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2000 the xine project + * + * This file is part of xine, a unix 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: demux_mpgaudio.c,v 1.1 2001/04/18 22:33:58 f1rmb Exp $ + * + * demultiplexer for mpeg audio (i.e. mp3) streams + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <string.h> +#include <stdlib.h> + +#include "xine.h" +#include "monitor.h" +#include "demux.h" + +#include "libmpg123/mpg123.h" +#include "libmpg123/mpglib.h" + +/* The following variable indicates the kind of error */ + +static uint32_t xine_debug; + +typedef struct _demux_mpgaudio_globals { + fifo_buffer_t *mBufAudio; + fifo_buffer_t *mBufVideo; + + input_plugin_t *mInput; + + pthread_t mThread; + + int mnStatus; +} demux_mpgaudio_globals_t ; + +static demux_mpgaudio_globals_t gDemuxMpgAudio; +static fifobuf_functions_t *Ffb; + +/* + * *********************************************************************** + * Adds some (very slightly hacked) parts of libmpg123 here: + * I don't want to link the lib to this demuxer. + */ +static int ssize; +static int grp_3tab[32 * 3] = {0,}; +static int grp_5tab[128 * 3] = {0,}; +static int grp_9tab[1024 * 3] = {0,}; +static real mpg123_muls[27][64]; +static int tabsel_123[2][3][16] = { + { + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}}, + + { + {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}} +}; +static long mpg123_freqs[9] = { + 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000 +}; +/* + * + */ +static void mpg123_init_layer2(void) { + static double mulmul[27] = { + 0.0, -2.0 / 3.0, 2.0 / 3.0, + 2.0 / 7.0, 2.0 / 15.0, 2.0 / 31.0, 2.0 / 63.0, 2.0 / 127.0, 2.0 / 255.0, + 2.0 / 511.0, 2.0 / 1023.0, 2.0 / 2047.0, 2.0 / 4095.0, 2.0 / 8191.0, + 2.0 / 16383.0, 2.0 / 32767.0, 2.0 / 65535.0, + -4.0 / 5.0, -2.0 / 5.0, 2.0 / 5.0, 4.0 / 5.0, + -8.0 / 9.0, -4.0 / 9.0, -2.0 / 9.0, 2.0 / 9.0, 4.0 / 9.0, 8.0 / 9.0 + }; + static int base[3][9] = { + {1, 0, 2,}, + {17, 18, 0, 19, 20,}, + {21, 1, 22, 23, 0, 24, 25, 2, 26} + }; + int i, j, k, l, len; + real *table; + static int tablen[3] = { 3, 5, 9 }; + static int *itable, *tables[3] = { grp_3tab, grp_5tab, grp_9tab }; + + for (i = 0; i < 3; i++) { + itable = tables[i]; + len = tablen[i]; + for (j = 0; j < len; j++) + for (k = 0; k < len; k++) + for (l = 0; l < len; l++) { + *itable++ = base[i][l]; + *itable++ = base[i][k]; + *itable++ = base[i][j]; + } + } + + for (k = 0; k < 27; k++) { + double m = mulmul[k]; + + table = mpg123_muls[k]; + for (j = 3, i = 0; i < 63; i++, j--) + *table++ = m * pow(2.0, (double) j / 3.0); + *table++ = 0.0; + } +} +/* + * + */ +static int mpg123_decode_header(struct frame *fr, unsigned long newhead) { + if (newhead & (1 << 20)) { + fr->lsf = (newhead & (1 << 19)) ? 0x0 : 0x1; + fr->mpeg25 = 0; + } + else { + fr->lsf = 1; + fr->mpeg25 = 1; + } + fr->lay = 4 - ((newhead >> 17) & 3); + if (fr->mpeg25) { + fr->sampling_frequency = 6 + ((newhead >> 10) & 0x3); + } + else + fr->sampling_frequency = ((newhead >> 10) & 0x3) + (fr->lsf * 3); + + fr->error_protection = ((newhead >> 16) & 0x1) ^ 0x1; + + if (fr->mpeg25) /* allow Bitrate change for 2.5 ... */ + fr->bitrate_index = ((newhead >> 12) & 0xf); + + fr->bitrate_index = ((newhead >> 12) & 0xf); + fr->padding = ((newhead >> 9) & 0x1); + fr->extension = ((newhead >> 8) & 0x1); + fr->mode = ((newhead >> 6) & 0x3); + fr->mode_ext = ((newhead >> 4) & 0x3); + fr->copyright = ((newhead >> 3) & 0x1); + fr->original = ((newhead >> 2) & 0x1); + fr->emphasis = newhead & 0x3; + + fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2; + + ssize = 0; + + if (!fr->bitrate_index) + return (0); + + switch (fr->lay) { + case 1: + mpg123_init_layer2(); /* inits also shared tables with layer1 */ + fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000; + fr->framesize /= mpg123_freqs[fr->sampling_frequency]; + fr->framesize = ((fr->framesize + fr->padding) << 2) - 4; + break; + case 2: + mpg123_init_layer2(); /* inits also shared tables with layer1 */ + fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000; + fr->framesize /= mpg123_freqs[fr->sampling_frequency]; + fr->framesize += fr->padding - 4; + break; + case 3: + if (fr->lsf) + ssize = (fr->stereo == 1) ? 9 : 17; + else + ssize = (fr->stereo == 1) ? 17 : 32; + if (fr->error_protection) + ssize += 2; + fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000; + fr->framesize /= mpg123_freqs[fr->sampling_frequency] << (fr->lsf); + fr->framesize = fr->framesize + fr->padding - 4; + break; + default: + return (0); + } + if(fr->framesize > MAXFRAMESIZE) + return 0; + return 1; +} +/* + * + */ +static int mpg123_head_check(unsigned long head) { + if ((head & 0xffe00000) != 0xffe00000) + return 0; + if (!((head >> 17) & 3)) + return 0; + if (((head >> 12) & 0xf) == 0xf) + return 0; + if (!((head >> 12) & 0xf)) + return 0; + if (((head >> 10) & 0x3) == 0x3) + return 0; + if (((head >> 19) & 1) == 1 + && ((head >> 17) & 3) == 3 + && ((head >> 16) & 1) == 1) + return 0; + if ((head & 0xffff0000) == 0xfffe0000) + return 0; + + return 1; +} +/* + * End of libmpg123 adds. + ************************************************************************ + */ + +int demux_mpgaudio_next (void) { + + buf_element_t *pBuf; + + pBuf = Ffb->buffer_pool_alloc (); + + pBuf->pContent = pBuf->pMem; + pBuf->nDTS = 0 ; /* FIXME ? */ + pBuf->nPTS = 0 ; /* FIXME ? */ + pBuf->nSize = gDemuxMpgAudio.mInput->read (pBuf->pMem, 2048) ; + pBuf->nType = BUF_MPEGAUDIO; /* FIXME */ + pBuf->nInputPos = gDemuxMpgAudio.mInput->seek (0, SEEK_CUR); + + Ffb->fifo_buffer_put (gDemuxMpgAudio.mBufAudio, pBuf); + + return (pBuf->nSize==2048); +} + +static void *demux_mpgaudio_loop (void *dummy) { + + buf_element_t *pBuf; + + do { + if (!demux_mpgaudio_next()) + gDemuxMpgAudio.mnStatus = DEMUX_FINISHED; + + } while (gDemuxMpgAudio.mnStatus == DEMUX_OK) ; + + xprintf (VERBOSE|DEMUX, "mpgaudio demux loop finished (status: %d)\n", + gDemuxMpgAudio.mnStatus); + + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_STREAMEND; + Ffb->fifo_buffer_put (gDemuxMpgAudio.mBufVideo, pBuf); + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_STREAMEND; + Ffb->fifo_buffer_put (gDemuxMpgAudio.mBufAudio, pBuf); + + return NULL; +} + +static void demux_mpgaudio_stop (void) { + void *p; + + gDemuxMpgAudio.mnStatus = DEMUX_FINISHED; + + Ffb->fifo_buffer_clear(gDemuxMpgAudio.mBufVideo); + Ffb->fifo_buffer_clear(gDemuxMpgAudio.mBufAudio); + + pthread_join (gDemuxMpgAudio.mThread, &p); +} + +static int demux_mpgaudio_get_status (void) { + return gDemuxMpgAudio.mnStatus; +} + +static void demux_mpgaudio_start (input_plugin_t *input_plugin, + fifo_buffer_t *bufVideo, + fifo_buffer_t *bufAudio, + fifo_buffer_t *bufSPU, off_t pos) +{ + buf_element_t *pBuf; + + gDemuxMpgAudio.mInput = input_plugin; + gDemuxMpgAudio.mBufVideo = bufVideo; + gDemuxMpgAudio.mBufAudio = bufAudio; + + gDemuxMpgAudio.mnStatus = DEMUX_OK; + + if((gDemuxMpgAudio.mInput->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) + gDemuxMpgAudio.mInput->seek (pos, SEEK_SET); + + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_RESET; + Ffb->fifo_buffer_put (gDemuxMpgAudio.mBufVideo, pBuf); + pBuf = Ffb->buffer_pool_alloc (); + pBuf->nType = BUF_RESET; + Ffb->fifo_buffer_put (gDemuxMpgAudio.mBufAudio, pBuf); + + pthread_create (&gDemuxMpgAudio.mThread, NULL, demux_mpgaudio_loop, NULL) ; +} + +static void demux_mpgaudio_select_audio_channel (int nChannel) { +} + +static void demux_mpgaudio_select_spu_channel (int nChannel) { +} + +static int demux_mpgaudio_open(input_plugin_t *ip, + const char *MRL, int stage) { + + switch(stage) { + + case STAGE_BY_CONTENT: { + uint8_t buf[4096]; + uint8_t *pbuf; + struct frame fr; + uint32_t head; + int in_buf, i; + int bs = 0; + + if(!ip) + return DEMUX_CANNOT_HANDLE; + + if((ip->get_capabilities() & INPUT_CAP_SEEKABLE) != 0) { + ip->seek(0, SEEK_SET); + + if(ip->get_blocksize) + bs = ip->get_blocksize(); + + if(bs > 4) + return DEMUX_CANNOT_HANDLE; + + if(!bs) + bs = 4; + + if(ip->read(buf, bs)) { + + /* Not an AVI ?? */ + if(buf[0] || buf[1] || (buf[2] != 0x01) || (buf[3] != 0x46)) { + + pbuf = (uint8_t *) malloc(1024); + head = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; + + while(!mpg123_head_check(head)) { + + in_buf = ip->read(pbuf, 1024); + + if(in_buf == 0) { + free(pbuf); + return DEMUX_CANNOT_HANDLE; + } + + for(i = 0; i < in_buf; i++) { + head <<= 8; + head |= pbuf[i]; + + if(mpg123_head_check(head)) { + ip->seek(i+1-in_buf, SEEK_CUR); + break; + } + } + } + free(pbuf); + + if(decode_header(&fr, head)) { + + if((ip->seek(fr.framesize, SEEK_CUR)) <= 0) + return DEMUX_CANNOT_HANDLE; + + if((ip->read(buf, 4)) != 4) + return DEMUX_CANNOT_HANDLE; + } + + head = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; + + if(mpg123_head_check(head) && + (((head >> 8) & 0x1) == 0x0) && (((head >> 6) & 0x3) == 0x1)) + return DEMUX_CAN_HANDLE; + } + } + } + return DEMUX_CANNOT_HANDLE; + } + break; + + case STAGE_BY_EXTENSION: { + char *suffix; + + suffix = strrchr(MRL, '.'); + xprintf(VERBOSE|DEMUX, "demux_mpgaudio_can_handle: suffix %s of %s\n", + suffix, MRL); + + if(!suffix) + return DEMUX_CANNOT_HANDLE; + + if(!strcasecmp(suffix, ".mp3") + || (!strcasecmp(suffix, ".mp2"))) { + return DEMUX_CAN_HANDLE; + } + } + break; + + default: + return DEMUX_CANNOT_HANDLE; + break; + } + + return DEMUX_CANNOT_HANDLE; +} + +static char *demux_mpgaudio_get_id(void) { + return "MPEGAUDIO"; +} + +static demux_functions_t demux_mpgaudio_functions = { + NULL, + NULL, + demux_mpgaudio_open, + demux_mpgaudio_start, + demux_mpgaudio_stop, + demux_mpgaudio_get_status, + demux_mpgaudio_select_audio_channel, + demux_mpgaudio_select_spu_channel, + demux_mpgaudio_get_id +}; + +demux_functions_t *init_demux_mpeg_audio(fifobuf_functions_t *f, uint32_t xd) { + + Ffb = f; + xine_debug = xd; + return &demux_mpgaudio_functions; +} |