/* * Copyright (C) 2000, 2001 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.39 2001/09/09 15:56:55 jkeil Exp $ * * demultiplexer for avi streams * * part of the code is taken from * avilib (C) 1999 Rainer Johanni * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "xine_internal.h" #include "monitor.h" #include "demux.h" #include "utils.h" #define WINE_TYPEDEFS_ONLY #include "libw32dll/wine/avifmt.h" #include "libw32dll/wine/windef.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; long flags; } 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; long dwScale_audio, dwRate_audio; long dwSampleSize; 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 */ uint32_t video_type; /* BUF_VIDEO_xxx type */ uint32_t audio_type; /* BUF_AUDIO_xxx type */ 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; } 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; int no_audio; uint32_t video_step; uint32_t AVI_errno; int send_end_buffers; char last_mrl[1024]; } 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 header list, corrupted ??? */ #define AVI_ERR_NO_MOVI 11 /* AVI file 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) \ do { \ this->AVI_errno = x; \ return 0; \ } while(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(demux_avi_t *this, 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) { this->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) { this->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 */ this->input->seek(this->input, 0, SEEK_SET); if( this->input->read(this->input, 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(this->input, 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(this->input, 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(this->input, hdrl_data,n) != n ) ERR_EXIT(AVI_ERR_READ); } else if(strncasecmp(data,"movi",4) == 0) { AVI->movi_start = this->input->seek(this->input, 0,SEEK_CUR); this->input->seek(this->input, n, SEEK_CUR); } else this->input->seek(this->input, 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(this->input, (char *)AVI->idx, n) != n ) ERR_EXIT(AVI_ERR_READ); } else this->input->seek(this->input, 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;icompressor,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; AVI->dwScale_audio = str2ulong(hdrl_data+i+20); AVI->dwRate_audio = str2ulong(hdrl_data+i+24); AVI->dwSampleSize = str2ulong(hdrl_data+i+44); 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); 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(this->input, 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;in_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(this->input, pos, SEEK_SET); if(this->input->read(this->input, 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(this->input, pos+AVI->movi_start-4, SEEK_SET); if(this->input->read(this->input, 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(this->input, AVI->movi_start, SEEK_SET); AVI->n_idx = 0; i=0; printf ("demux_avi: reconstructing index"); while(1) { if( this->input->read(this->input, data,8) != 8 ) break; n = str2ulong(data+4); i++; if (i>1000) { printf ("."); i = 0; fflush (stdout); } /* The movi list may contain sub-lists, ignore them */ if(strncasecmp(data,"LIST",4)==0) { this->input->seek(this->input, 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(this, AVI, data, AVIIF_KEYFRAME, this->input->seek(this->input, 0, SEEK_CUR)-8, n); } this->input->seek(this->input, PAD_EVEN(n), SEEK_CUR); } printf ("done\n"); idx_type = 1; } /* Now generate the video index and audio index arrays */ nvi = 0; nai = 0; for(i=0;in_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;in_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); AVI->video_index[nvi].flags = str2ulong(AVI->idx[i]+ 4); 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(this->input, AVI->movi_start, SEEK_SET); AVI->video_posf = 0; AVI->video_posb = 0; return AVI; } static void AVI_seek_start(avi_t *AVI) { AVI->video_posf = 0; AVI->video_posb = 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) { this->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 = 2; return nr; } left = AVI->audio_index[AVI->audio_posc].len - AVI->audio_posb; } if(bytesaudio_index[AVI->audio_posc].pos + AVI->audio_posb; /* printf ("demux_avi: read audio from %d\n", pos); */ if (this->input->seek (this->input, pos, SEEK_SET)<0) return -1; if (this->input->read(this->input, audbuf+nr,todo) != todo) { this->AVI_errno = AVI_ERR_READ; *bFrameDone = 1; return -1; } bytes -= todo; nr += todo; AVI->audio_posb += todo; } left = AVI->audio_index[AVI->audio_posc].len - AVI->audio_posb; if (left==0) *bFrameDone = 2; else *bFrameDone = 1; 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) { this->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 = 2; return nr; } left = AVI->video_index[AVI->video_posf].len - AVI->video_posb; } if(bytesvideo_index[AVI->video_posf].pos + AVI->video_posb; /* printf ("demux_avi: read video from %d\n", pos); */ if (this->input->seek (this->input, pos, SEEK_SET)<0) return -1; if (this->input->read(this->input, vidbuf+nr,todo) != todo) { this->AVI_errno = AVI_ERR_READ; *bFrameDone = 1; return -1; } bytes -= todo; nr += todo; AVI->video_posb += todo; } left = AVI->video_index[AVI->video_posf].len - AVI->video_posb; if (left==0) *bFrameDone = 2; else *bFrameDone = 1; return nr; } static uint32_t get_audio_pts (demux_avi_t *this, long posc, long posb) { if (this->avi->dwSampleSize==0) return posc * (double) this->avi->dwScale_audio / this->avi->dwRate_audio * 90000.0; else return (this->avi->audio_index[posc].tot+posb)/this->avi->dwSampleSize * (double) this->avi->dwScale_audio / this->avi->dwRate_audio * 90000.0; } static uint32_t get_video_pts (demux_avi_t *this, long pos) { return pos * (double) this->avi->dwScale / this->avi->dwRate * 90000.0; } static int demux_avi_next (demux_avi_t *this) { buf_element_t *buf = NULL; uint32_t audio_pts, video_pts; if (this->avi->video_frames <= this->avi->video_posf) return 0; if (!this->no_audio && (this->avi->audio_chunks <= this->avi->audio_posc)) return 0; buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->content = buf->mem; audio_pts = get_audio_pts (this, this->avi->audio_posc, this->avi->audio_posb); video_pts = get_video_pts (this, this->avi->video_posf); if (!this->no_audio && (audio_pts < video_pts)) { /* read audio */ xprintf (VERBOSE|DEMUX|VAVI, "demux_avi: audio \n"); buf->PTS = audio_pts; buf->size = AVI_read_audio (this, this->avi, buf->mem, 2048, &buf->decoder_info[0]); if (buf->size<0) { buf->free_buffer (buf); return 0; } buf->input_pos = 0; buf->input_time = 0; buf->type = this->avi->audio_type; buf->decoder_info[1] = this->avi->a_rate; /* Audio Rate */ buf->decoder_info[2] = this->avi->a_bits; /* Audio bits */ buf->decoder_info[3] = this->avi->a_chans; /* Audio channels */ if(this->audio_fifo) { this->audio_fifo->put (this->audio_fifo, buf); } else { buf->free_buffer (buf); } } else { /* read video */ xprintf (VERBOSE|DEMUX|VAVI, "demux_avi: video \n"); buf->PTS = video_pts; buf->size = AVI_read_video (this, this->avi, buf->mem, 2048, &buf->decoder_info[0]); buf->type = this->avi->video_type; buf->input_time = video_pts / 90000; buf->input_pos = this->input->get_current_pos(this->input); if (buf->size<0) { buf->free_buffer (buf); return 0; } /* printf ("demux_avi: adding buf %d to video fifo, decoder_info[0]: %d\n", buf, buf->decoder_info[0]); */ 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 = NULL; demux_avi_t *this = (demux_avi_t *) this_gen; this->send_end_buffers = 1; do { /* printf ("avi loop (status %d)\n", this->status); */ if (!demux_avi_next(this)) this->status = DEMUX_FINISHED; } while (this->status == DEMUX_OK) ; if (this->send_end_buffers) { buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->type = BUF_CONTROL_END; buf->decoder_info[0] = 0; /* stream finished */ this->video_fifo->put (this->video_fifo, buf); if(this->audio_fifo) { buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->type = BUF_CONTROL_END; buf->decoder_info[0] = 0; /* stream finished */ this->audio_fifo->put (this->audio_fifo, buf); } } printf ("demux_avi: demux loop finished.\n"); pthread_exit(NULL); return NULL; } static void demux_avi_stop (demux_plugin_t *this_gen) { demux_avi_t *this = (demux_avi_t *) this_gen; buf_element_t *buf; void *p; if (this->status != DEMUX_OK) { printf ("demux_avi: stop...ignored\n"); return; } this->send_end_buffers = 0; this->status = DEMUX_FINISHED; pthread_cancel (this->thread); pthread_join (this->thread, &p); /* AVI_close (this->avi); this->avi = NULL; */ this->video_fifo->clear(this->video_fifo); if (this->audio_fifo) this->audio_fifo->clear(this->audio_fifo); buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->type = BUF_CONTROL_END; buf->decoder_info[0] = 1; /* forced */ this->video_fifo->put (this->video_fifo, buf); if(this->audio_fifo) { buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->type = BUF_CONTROL_END; buf->decoder_info[0] = 1; /* forced */ this->audio_fifo->put (this->audio_fifo, buf); } } 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 *video_fifo, fifo_buffer_t *audio_fifo, off_t start_pos, int start_time, gui_get_next_mrl_cb_t next_mrl_cb, gui_branched_cb_t branched_cb) { buf_element_t *buf; demux_avi_t *this = (demux_avi_t *) this_gen; uint32_t video_pts = 0; this->audio_fifo = audio_fifo; this->video_fifo = video_fifo; this->status = DEMUX_OK; printf ("demux_avi: video format = %s, audio format = 0x%lx\n", this->avi->compressor, this->avi->a_fmt); this->no_audio = 0; switch (this->avi->a_fmt) { case 0x01: this->avi->audio_type = BUF_AUDIO_LPCM_LE; break; case 0x2000: this->avi->audio_type = BUF_AUDIO_A52; break; case 0x50: case 0x55: this->avi->audio_type = BUF_AUDIO_MPEG; break; case 0x160: case 0x161: this->avi->audio_type = BUF_AUDIO_DIVXA; break; case 0x02: this->avi->audio_type = BUF_AUDIO_MSADPCM; break; case 0x11: this->avi->audio_type = BUF_AUDIO_IMAADPCM; break; case 0x31: case 0x32: this->avi->audio_type = BUF_AUDIO_MSGSM; break; default: printf ("demux_avi: unknown audio type 0x%lx\n", this->avi->a_fmt); this->no_audio = 1; this->avi->audio_type = BUF_AUDIO_MPEG; break; } AVI_seek_start (this->avi); /* * seek to start pos / time */ /* seek video */ if (start_pos) { while ( (this->avi->video_index[this->avi->video_posf].pos < start_pos) || !(this->avi->video_index[this->avi->video_posf].flags & AVIIF_KEYFRAME) ) { this->avi->video_posf++; if (this->avi->video_posf>this->avi->video_frames) { this->status = DEMUX_FINISHED; printf ("demux_avi: video seek to start failed\n"); return; } } video_pts = get_video_pts (this, this->avi->video_posf); } else if (start_time) { video_pts = start_time * 90000; while ( (get_video_pts (this, this->avi->video_posf) < video_pts) || !(this->avi->video_index[this->avi->video_posf].flags & AVIIF_KEYFRAME) ) { this->avi->video_posf++; if (this->avi->video_posf>this->avi->video_frames) { this->status = DEMUX_FINISHED; printf ("demux_avi: video seek to start failed\n"); return; } } } /* seek audio */ if (!this->no_audio) { while (get_audio_pts (this, this->avi->audio_posc, 0) < video_pts) { this->avi->audio_posc++; if (this->avi->audio_posc>this->avi->audio_chunks) { this->status = DEMUX_FINISHED; printf ("demux_avi: audio seek to start failed\n"); return; } } } /* * send start buffers */ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->type = BUF_CONTROL_START; this->video_fifo->put (this->video_fifo, buf); if(this->audio_fifo) { buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->type = BUF_CONTROL_START; this->audio_fifo->put (this->audio_fifo, buf); } buf = this->video_fifo->buffer_pool_alloc (this->video_fifo); buf->content = buf->mem; buf->decoder_info[0] = 0; /* first package, containing bih */ buf->decoder_info[1] = this->video_step; memcpy (buf->content, &this->avi->bih, sizeof (this->avi->bih)); buf->size = sizeof (this->avi->bih); switch (str2ulong((void*)&this->avi->bih.biCompression)) { case mmioFOURCC('M', 'P', 'G', '4'): case mmioFOURCC('m', 'p', 'g', '4'): case mmioFOURCC('M', 'P', '4', '1'): case mmioFOURCC('m', 'p', '4', '1'): case mmioFOURCC('M', 'P', '4', '2'): case mmioFOURCC('m', 'p', '4', '2'): case mmioFOURCC('M', 'P', '4', '3'): case mmioFOURCC('m', 'p', '4', '3'): case mmioFOURCC('D', 'I', 'V', '3'): case mmioFOURCC('d', 'i', 'v', '3'): case mmioFOURCC('D', 'I', 'V', '4'): case mmioFOURCC('d', 'i', 'v', '4'): /* Video in Microsoft MPEG-4 format */ this->avi->video_type = BUF_VIDEO_MSMPEG4; break; case mmioFOURCC('D', 'I', 'V', 'X'): case mmioFOURCC('d', 'i', 'v', 'x'): case mmioFOURCC('D', 'i', 'v', 'x'): case mmioFOURCC('D', 'i', 'v', 'X'): /* Video in mpeg4 (opendivx) format */ this->avi->video_type = BUF_VIDEO_MPEG4; break; case mmioFOURCC('d', 'm', 'b', '1'): case mmioFOURCC('M', 'J', 'P', 'G'): /* Video in motion jpeg format */ this->avi->video_type = BUF_VIDEO_MJPEG; break; case mmioFOURCC('I', 'V', '5', '0'): case mmioFOURCC('i', 'v', '5', '0'): /* Video in Indeo Video 5.0 format */ this->avi->video_type = BUF_VIDEO_IV50; break; case mmioFOURCC('I', 'V', '4', '1'): case mmioFOURCC('i', 'v', '4', '1'): /* Video in Indeo Video 4.1 format */ this->avi->video_type = BUF_VIDEO_IV41; break; case mmioFOURCC('I', 'V', '3', '2'): case mmioFOURCC('i', 'v', '3', '2'): /* Video in Indeo Video 3.2 format */ this->avi->video_type = BUF_VIDEO_IV32; break; case mmioFOURCC('I', 'V', '3', '1'): case mmioFOURCC('i', 'v', '3', '1'): /* Video in Indeo Video 3.1 format */ this->avi->video_type = BUF_VIDEO_IV31; break; case mmioFOURCC('c', 'v', 'i', 'd'): /* Video in Cinepak format */ this->avi->video_type = BUF_VIDEO_CINEPACK; break; case mmioFOURCC('V', 'C', 'R', '1'): /* Video in ATI VCR1 format */ this->avi->video_type = BUF_VIDEO_ATIVCR1; break; case mmioFOURCC('V', 'C', 'R', '2'): /* Video in ATI VCR2 format */ this->avi->video_type = BUF_VIDEO_ATIVCR2; break; case mmioFOURCC('I', '2', '6', '3'): case mmioFOURCC('i', '2', '6', '3'): /* Video in I263 format */ this->avi->video_type = BUF_VIDEO_I263; break; default: printf ("demux_avi: unknown avi format %.4s\n", (char*)&this->avi->bih.biCompression); this->status = DEMUX_FINISHED; return; } buf->type = this->avi->video_type; this->video_fifo->put (this->video_fifo, buf); if(this->audio_fifo) { buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); buf->content = buf->mem; memcpy (buf->content, &this->avi->wavex, sizeof (this->avi->wavex)); buf->size = sizeof (this->avi->wavex); buf->type = this->avi->audio_type; buf->decoder_info[0] = 0; /* first package, containing wavex */ buf->decoder_info[1] = this->avi->a_rate; /* Audio Rate */ buf->decoder_info[2] = this->avi->a_bits; /* Audio bits */ buf->decoder_info[3] = this->avi->a_chans; /* Audio bits */ 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: { if (input->get_blocksize(input)) return DEMUX_CANNOT_HANDLE; if (!(input->get_capabilities(input) & INPUT_CAP_SEEKABLE)) return DEMUX_CANNOT_HANDLE; input->seek(input, 0, SEEK_SET); this->input = input; if (strncmp(this->last_mrl, input->get_mrl (input), 1024)) { if (this->avi) AVI_close (this->avi); this->avi = AVI_init (this); } if (this->avi) { printf ("demux_avi: %ld frames\n", this->avi->video_frames); strncpy(this->last_mrl, input->get_mrl (input), 1024); return DEMUX_CAN_HANDLE; } else printf ("demux_avi: AVI_init failed (AVI_errno: %d)\n",this->AVI_errno); return DEMUX_CANNOT_HANDLE; } break; case STAGE_BY_EXTENSION: { char *ending, *mrl; mrl = input->get_mrl (input); 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; if (strncmp(this->last_mrl, input->get_mrl (input), 1024)) { if (this->avi) AVI_close (this->avi); this->avi = AVI_init (this); } if (this->avi) { strncpy(this->last_mrl, input->get_mrl (input), 1024); return DEMUX_CAN_HANDLE; } else { printf ("demux_avi: AVI_init failed (AVI_errno: %d)\n",this->AVI_errno); 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(void) { return "AVI"; } static int demux_avi_get_stream_length (demux_plugin_t *this_gen) { demux_avi_t *this = (demux_avi_t *) this_gen; if (this->avi) { return get_video_pts(this, this->avi->video_frames) / 90000 ; } return 0; } demux_plugin_t *init_demuxer_plugin(int iface, config_values_t *config) { demux_avi_t *this; if (iface != 3) { printf( "demux_avi: this plugin doesn't support plugin API version %d.\n" "demux_avi: this means there's a version mismatch between xine and this " "demux_avi: demuxer plugin.\nInstalling current demuxer plugins should help.\n", iface); return NULL; } this = xmalloc (sizeof (demux_avi_t)); xine_debug = config->lookup_int (config, "xine_debug", 0); this->demux_plugin.interface_version = DEMUXER_PLUGIN_IFACE_VERSION; 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; this->demux_plugin.get_stream_length = demux_avi_get_stream_length; return (demux_plugin_t *) this; }