AviDecaps.cpp
上传用户:lusi_8715
上传日期:2007-01-08
资源大小:199k
文件大小:21k
- /**************************************************************************************
- * *
- * This application contains code from OpenDivX and is released as a "Larger Work" *
- * under that license. Consistant with that license, this application is released *
- * under the GNU General Public License. *
- * *
- * The OpenDivX license can be found at: http://www.projectmayo.com/opendivx/docs.php *
- * The GPL can be found at: http://www.gnu.org/copyleft/gpl.html *
- * *
- * Copyright (c) 2001 - Project Mayo *
- * *
- * Authors: Damien Chavarria <adrc at projectmayo.com> *
- * *
- **************************************************************************************/
- #include "AviDecaps.h"
- /*
- * Some useful functions
- */
- /*
- * copy n into dst as a 4 byte,
- * little endian number.
- * should also work on
- * big endian machines
- *
- */
- static void long2str(char *dst, int n)
- {
- dst[0] = (n ) & 0xff;
- dst[1] = (n >> 8) & 0xff;
- dst[2] = (n >> 16) & 0xff;
- dst[3] = (n >> 24) & 0xff;
- }
- /* Convert a string of 4
- * or 2 bytes to a number,
- * also working on big
- * endian machines
- *
- */
- unsigned long str2ulong(char *str)
- {
- unsigned long result;
- result = ( ((unsigned long) str[0] & 0xFF) | ( ((unsigned long)str[1] &0xFF) << 8) |
- (((unsigned long) str[2] & 0xFF) << 16) | ( ((unsigned long) str[3] & 0xFF) << 24) );
- return result;
- }
- static unsigned long str2ushort(char *str)
- {
- return ( str[0] | (str[1]<<8) );
- }
- /*
- * "SMART" AVI Readind Class
- */
- /*
- * Constuctor : opens the file and init
- * the Mutex for reading
- */
- AviDecaps::AviDecaps()
- {
- this->video_pos = 0;
- this->audio_posc = 0;
- this->audio_posb = 0;
- this->idx = NULL;
- this->video_index = NULL;
- this->audio_index = NULL;
- this->input = NULL;
- }
- /*
- * Desturctor : Cleanup.
- *
- */
- AviDecaps::~AviDecaps()
- {
- delete this->input;
- }
- /*
- * Reads the first 12 bytes of
- * the file, and returns TRUE
- * if the file is an AVI.
- *
- */
- int AviDecaps::IsAVI()
- {
- char data[24];
-
- if(this->input != NULL) {
-
- if( this->input->Read(data, 12) != 12 ) {
-
- MessageBox(NULL, "Error Reading", "Info", MB_OK);
- return 0;
- }
-
- if( strncmp(data, "RIFF", 4) !=0 ||
- strncmp(data + 8, "AVI ", 4) !=0 ) {
- return 0;
- }
-
- return 1;
- }
-
- return 0;
- }
- /*
- * Returns the sample size of
- * the first audio stream...
- *
- */
- int AviDecaps::SampleSize()
- {
- int s;
-
- s = ((this->a_bits + 7) / 8) * this->a_chans;
-
- if(s == 0)
- s = 1;
-
- return s;
- }
- /*
- * Add an entry to the gloabl index
- * this function is used while reading.
- *
- */
- int AviDecaps::AddIndexEntry(char *tag,
- long flags,
- long pos,
- long len)
- {
- void *ptr;
-
- if(this->n_idx >= this->max_idx)
- {
- ptr = realloc((void *)this->idx,
- (this->max_idx + 4096) * 16);
-
- if(ptr == 0) {
- return -1;
- }
-
- this->max_idx += 4096;
- this->idx = (char((*)[16]) ) ptr;
- }
-
- /*
- * add the index entry
- *
- */
-
- memcpy(this->idx[this->n_idx], tag, 4);
-
- long2str(this->idx[this->n_idx]+ 4, flags);
-
- long2str(this->idx[this->n_idx]+ 8, pos);
-
- long2str(this->idx[this->n_idx]+12, len);
-
- /*
- * update counter
- *
- */
-
- this->n_idx++;
-
- return 0;
- }
- /*
- * Fill the class with info from headers
- * and reconstruct an index if wanted.
- *
- */
- int AviDecaps::FillHeader(int getIndex)
- {
- unsigned long i, n, rate;
- long scale, idx_type;
- 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];
-
- /* 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(strncmp(data, "LIST", 4) == 0)
- {
- if( this->input->Read(data, 4) != 4 ) {
- return 0;
- }
-
- n -= 4;
-
- if(strncmp(data, "hdrl", 4) == 0)
- {
- hdrl_len = n;
-
- hdrl_data = (char *) malloc(n);
-
- if(hdrl_data == 0) {
-
- return 0;
- }
-
- if( this->input->Read(hdrl_data, n) != n ) {
-
- return 0;
- }
- }
- else if(strncmp(data, "movi", 4) == 0)
- {
- this->movi_start = this->input->Seek(0, INPUT_SEEK_CUR);
-
- this->input->Seek(n, INPUT_SEEK_CUR);
- }
- else
- this->input->Seek(n, INPUT_SEEK_CUR);
- }
- else if(strncmp(data,"idx1",4) == 0)
- {
- /*
- * n must be a multiple of 16,
- * but the reading does not
- * break if this is not the case
- *
- */
-
- this->n_idx = this->max_idx = n/16;
- this->idx = (char((*)[16]) ) malloc(n);
-
- if(this->idx == NULL) {
-
- return 0;
- }
-
- unsigned long read;
- if( (read = this->input->Read((char *) this->idx, n)) != n ) {
-
- break;
- }
- }
- else {
-
- this->input->Seek(n, INPUT_SEEK_CUR);
- }
- }
-
- if(!hdrl_data) {
-
- return 0;
- }
-
- if(!this->movi_start) {
-
- return 0;
- }
-
- /*
- * interpret the header list
- *
- */
-
- for(i=0; i < hdrl_len; )
- {
- /*
- * list tags are completly ignored
- *
- */
-
- if(strncmp(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(strncmp(hdrl_data + i, "strh", 4)==0)
- {
- i += 8;
- if(strncmp(hdrl_data + i, "vids", 4) == 0 && !vids_strh_seen)
- {
- memcpy(this->compressor,hdrl_data+i+4,4);
-
- this->compressor[4] = 0;
-
- scale = str2ulong(hdrl_data+i+20);
- rate = str2ulong(hdrl_data+i+24);
-
- if(scale!=0)
- this->fps = (double)rate/(double)scale;
-
- this->video_frames = str2ulong(hdrl_data+i+32);
- this->video_strn = num_stream;
-
- vids_strh_seen = 1;
- lasttag = 1;
- }
- else if (strncmp (hdrl_data + i, "auds", 4) == 0 && ! auds_strh_seen)
- {
- this->audio_bytes = str2ulong(hdrl_data + i + 32)*this->SampleSize();
- this->audio_strn = num_stream;
-
- auds_strh_seen = 1;
- lasttag = 2;
- }
- else
- lasttag = 0;
- num_stream++;
- }
- else if(strncmp(hdrl_data + i, "strf", 4)==0)
- {
- i += 8;
- if(lasttag == 1)
- {
-
- /*
- * keep a copy of
- * the bitmapinfoheader
- *
- */
-
- memcpy(&this->bitmapinfoheader,
- hdrl_data + i,
- sizeof(BITMAPINFOHEADER));
-
- this->width = str2ulong(hdrl_data+i+4);
- this->height = str2ulong(hdrl_data+i+8);
- vids_strf_seen = 1;
- }
- else if(lasttag == 2)
- {
- /*
- * keep a copy of the WAVEFORMATEX
- */
- memcpy(&this->waveformatex,
- hdrl_data + i,
- sizeof(WAVEFORMATEX));
- this->a_fmt = str2ushort(hdrl_data + i );
- this->a_chans = str2ushort(hdrl_data + i + 2);
- this->a_rate = str2ulong (hdrl_data + i + 4);
- this->a_bits = str2ushort(hdrl_data + i + 14) == 0 ? 16 : 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 || this->video_frames==0) {
-
- return 0;
- }
-
- this->video_tag[0] = this->video_strn/10 + '0';
- this->video_tag[1] = this->video_strn%10 + '0';
- this->video_tag[2] = 'd';
- this->video_tag[3] = 'b';
-
- /*
- * audio tag is set to "99wb"
- * if no audio present
- *
- */
-
- if(!this->a_chans)
- this->audio_strn = 99;
-
- this->audio_tag[0] = this->audio_strn/10 + '0';
- this->audio_tag[1] = this->audio_strn%10 + '0';
- this->audio_tag[2] = 'w';
- this->audio_tag[3] = 'b';
-
- this->input->Seek(this->movi_start, INPUT_SEEK_SET);
-
- /*
- * get index if wanted
- *
- */
-
- if(!getIndex)
- return 1;
-
- /*
- * 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(this->idx)
- {
- long pos, len;
-
- /*
- * search the first videoframe
- * in the idx1 and look where
- * it is in the file
- *
- */
-
- for(i=0;i<this->n_idx;i++) {
- if( strncmp(this->idx[i], this->video_tag, 3) == 0 )
- break;
- }
-
- if(i >= this->n_idx)
- return 0;
-
- pos = str2ulong(this->idx[i] + 8);
- len = str2ulong(this->idx[i] + 12);
-
- this->input->Seek(pos, INPUT_SEEK_SET);
-
- if(this->input->Read(data, 8)!=8)
- return 0;
-
- if( strncmp(data, this->idx[i], 4) == 0 && str2ulong(data + 4) == len )
- {
- /*
- * index from start of file
- *
- */
-
- idx_type = 1;
- }
- else
- {
- this->input->Seek(pos + this->movi_start - 4,INPUT_SEEK_SET);
-
- if(this->input->Read(data, 8) != 8)
- return 0;
-
- if( strncmp(data, this->idx[i], 4) == 0 && str2ulong(data + 4) == len )
- {
- /*
- * index from start of movi list
- *
- */
-
- idx_type = 2;
- }
- }
- /*
- * 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->movi_start, INPUT_SEEK_SET);
-
- this->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(strncmp(data,"LIST", 4) == 0)
- {
- this->input->Seek(4, INPUT_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') ) )
- {
- this->AddIndexEntry(data,
- 0,
- this->input->Seek(0, INPUT_SEEK_CUR) - 8,
- n);
- }
-
- this->input->Seek(PAD_EVEN(n),
- INPUT_SEEK_CUR);
- }
- idx_type = 1;
- }
-
- /*
- * now generate the video index
- * and audio index arrays
- *
- */
-
- nvi = 0;
- nai = 0;
-
- for(i=0; i < this->n_idx; i++)
- {
- if(strncmp(this->idx[i], this->video_tag, 3) == 0)
- nvi++;
-
- if(strncmp(this->idx[i], this->audio_tag, 4) == 0)
- nai++;
- }
-
- this->video_frames = nvi;
- this->audio_chunks = nai;
-
- if(this->video_frames == 0)
- return 0;
-
- this->video_index = (video_index_entry *) malloc(nvi*sizeof(video_index_entry));
-
- if(this->video_index == 0)
- return 0;
-
- if(this->audio_chunks)
- {
- this->audio_index = (audio_index_entry *) malloc(nai*sizeof(audio_index_entry));
-
- if(this->audio_index==0)
- return 0;
- }
-
- nvi = 0;
- nai = 0;
- tot = 0;
-
- ioff = idx_type == 1 ? 8 : this->movi_start+4;
-
- for(i=0; i < this->n_idx; i++)
- {
- if(strncmp(this->idx[i], this->video_tag, 3) == 0)
- {
- this->video_index[nvi].flags = str2ulong(this->idx[i]+ 4);
- this->video_index[nvi].pos = str2ulong(this->idx[i]+ 8)+ioff;
- this->video_index[nvi].len = str2ulong(this->idx[i]+12);
-
- nvi++;
- }
- if(strncmp(this->idx[i], this->audio_tag, 4) == 0)
- {
- this->audio_index[nai].pos = str2ulong(this->idx[i]+ 8)+ioff;
- this->audio_index[nai].len = str2ulong(this->idx[i]+12);
- this->audio_index[nai].tot = tot;
-
- tot += this->audio_index[nai].len;
-
- nai++;
- }
- }
-
- this->audio_bytes = tot;
-
- /*
- * reposition the file
- *
- */
-
- this->input->Seek(this->movi_start,
- INPUT_SEEK_SET);
-
- this->video_pos = 0;
-
- return 1;
- }
- /*
- * Reading Functions
- *
- */
- /*
- * Tries to open an AVI
- * with and without an index
- */
- int AviDecaps::Open(char *lpFilename, int type)
- {
- if(lpFilename) {
-
- this->input = new InputMedia();
- this->hIOMutex = CreateMutex (NULL, FALSE, NULL);
- }
- if(!this->input->Open(lpFilename, INPUT_OPEN_BINARY, type)) {
- delete this->input;
- return 0;
- }
- this->video_pos = 0;
- this->audio_posc = 0;
- this->audio_posb = 0;
- this->idx = NULL;
- this->video_index = NULL;
- this->audio_index = NULL;
- if(!this->input->isOK()) {
- delete this->input;
- this->input = NULL;
- return 0;
- }
- if(this->IsAVI()) {
-
- if(this->FillHeader(1)) {
-
- return 1;
- }
- else {
- this->input->Seek(0, INPUT_SEEK_SET);
- this->IsAVI();
-
- if(this->FillHeader(0)) {
-
- return 1;
- }
- }
- }
-
- return 0;
- }
- /*
- * Returns the video width
- *
- */
- int AviDecaps::Width()
- {
- return this->width;
- }
- /*
- * Returns the video height
- *
- */
- int AviDecaps::Height()
- {
- return this->height;
- }
- /*
- * Gives the size of
- * a particular frame
- */
- int AviDecaps::FrameSize(unsigned long frame_number)
- {
- if(!this->video_index)
- {
- return -1;
- }
- if(frame_number < 0 || frame_number >= this->video_frames) {
- return -1;
- }
- return(this->video_index[frame_number].len);
- }
- /*
- * Returns the total
- * number of video streams
- *
- */
- int AviDecaps::VideoStreams()
- {
- return this->video_strn;
- }
- /*
- * Returns the total
- * number of audio streams
- *
- */
- int AviDecaps::AudioStreams()
- {
- return this->audio_strn;
- }
- /*
- * Returns the bitmapinfoheader
- * associated with the first video
- * stream.
- */
- BITMAPINFOHEADER *AviDecaps::BitmapInfoHeader()
- {
- return &this->bitmapinfoheader;
- }
- /*
- * Returns the wavefromatex
- * associated with the first audio
- * stream.
- */
-
- WAVEFORMATEX *AviDecaps::WaveFormatEx()
- {
- return &this->waveformatex;
- }
- /*
- * Reads the next video Frame into
- * buffer, return the actual size of
- * the frame.
- *
- */
- int AviDecaps::NextVideoFrame(char *buffer)
- {
- int m;
- unsigned int n;
- /*
- * Security checks
- *
- */
- if(!this->video_index)
- {
- return -1;
- }
- if(this->video_pos < 0 || this->video_pos >= this->video_frames) {
-
- return -2;
- }
- /*
- * Request the mutex
- * for reading the file;
- */
- WaitForSingleObject(this->hIOMutex, INFINITE);
- n = this->video_index[this->video_pos].len;
- this->input->Seek(this->video_index[this->video_pos].pos, INPUT_SEEK_SET);
- if ((m = this->input->Read(buffer, n)) != n)
- {
- this->video_pos++;
- ReleaseMutex(this->hIOMutex);
- return n;
- }
- this->video_pos++;
- /*
- * Release the Mutex.
- */
- ReleaseMutex(this->hIOMutex);
- return n;
- }
- /*
- * Reads any amount of audio
- * data. FIXME : should return
- * the actual number read.
- */
- int AviDecaps::ReadAudio(char *audbuf, int bytes)
- {
- int nr, pos, left = 0, todo;
- if(!this->audio_index)
- {
- MessageBox(NULL, "No audio index", "", MB_OK);
- return -1;
- }
- nr = 0;
- /*
- * Request the read Mutex
- */
- WaitForSingleObject(this->hIOMutex, INFINITE);
- /*
- * We loop until we parsed enough
- * chunks for the amount we want
- *
- */
- while(bytes > 0)
- {
- left = this->audio_index[this->audio_posc].len - this->audio_posb;
- if(left == 0)
- {
- if(this->audio_posc>=this->audio_chunks-1)
- {
- ReleaseMutex(this->hIOMutex);
- return nr;
- }
- this->audio_posc++;
- this->audio_posb = 0;
- continue;
- }
- if(bytes<left)
- todo = bytes;
- else
- todo = left;
- pos = this->audio_index[this->audio_posc].pos + this->audio_posb;
-
- this->input->Seek(pos, INPUT_SEEK_SET);
- if (this->input->Read(audbuf + nr, todo) != todo)
- {
- MessageBox(NULL, "Can't read enough!", "", MB_OK);
- ReleaseMutex(this->hIOMutex);
- return -1;
- }
- bytes -= todo;
- nr += todo;
- this->audio_posb += todo;
- }
- /*
- * And release the Mutex.
- */
- ReleaseMutex(this->hIOMutex);
- return nr;
- }
- /*
- * Return the actual framerate
- * FIXME : should be a double...
- *
- */
- double AviDecaps::FrameRate()
- {
- /*
- * Fix for some trailers
- */
- if(this->fps == 0)
- this->fps = 25;
- if(this->fps == 23)
- this->fps = 25;
- return (double) this->fps;
- }
- long AviDecaps::TotalFrames() {
- return this->video_frames;
- }
- /*
- * Seek to a particular
- * video frame.
- */
- int AviDecaps::VideoSeek(long frame)
- {
- if(!this->video_index) {
-
- return -1;
- }
- if (frame < 0 ) frame = 0;
-
- this->video_pos = frame;
- return 1;
- }
- int AviDecaps::AudioSeek(long bytes)
- {
- long n0, n1, n;
- if(!this->audio_index) {
- return -1;
- }
- if(bytes < 0)
- bytes = 0;
- n0 = 0;
- n1 = this->audio_chunks;
- while(n0 < n1 - 1)
- {
- n = (n0 + n1) / 2;
- if(this->audio_index[n].tot > bytes)
- n1 = n;
- else
- n0 = n;
- }
- this->audio_posc = n0;
- if(this->audio_index[n0].len > 1000) {
- this->audio_posb = bytes - this->audio_index[n0].tot;
- }
- else {
- this->audio_posb = 0;
- }
- return 0;
- }
- BOOL AviDecaps::isKeyframe(long frame)
- {
- if(frame < 0)
- frame = 0;
- if(!this->video_index)
- {
- /*
- * we still return 1 to avoid looping
- * on waiting for a keyframe.
- *
- */
- return 1;
- }
- return this->video_index[frame].flags & AVIIF_KEYFRAME;
- }
- int AviDecaps::NextKeyFrame()
- {
- WaitForSingleObject(this->hIOMutex, INFINITE);
- /*
- * Allways increment by one
- */
- this->video_pos++;
- while(!isKeyframe(this->video_pos) && this->video_pos < this->video_frames)
- this->video_pos++;
- ReleaseMutex(this->hIOMutex);
- return 1;
- }
- int AviDecaps::PreviousKeyFrame()
- {
- WaitForSingleObject(this->hIOMutex, INFINITE);
- /*
- * Allways decrement by two
- * since we read the last frame
- */
- this->video_pos--;
- this->video_pos--;
- while(!isKeyframe(this->video_pos) && this->video_pos > 0)
- this->video_pos--;
- ReleaseMutex(this->hIOMutex);
- return 1;
- }
- int AviDecaps::Seek(int percent)
- {
- long frame;
- double ratio;
- long audio_bytes;
- char msg[256];
-
- WaitForSingleObject(this->hIOMutex, INFINITE);
- /*
- * compute the desired
- * frame number
- *
- */
- frame = (long) (percent * this->video_frames / 100);
- /*
- * and go to the next
- * keyframe.
- *
- */
-
- while(!this->isKeyframe(frame)) {
- frame++;
- }
- /*
- * now set video
- * position.
- *
- */
-
- this->VideoSeek(frame);
- /*
- * calculate what ratio
- * it corresponds to
- *
- */
- if(this->AudioStreams() > 0) {
-
- ratio = (double) ((double) frame / (double) this->video_frames);
-
- /*
- * and set audio
- * position
- *
- */
- audio_bytes = (long) (ratio * this->audio_bytes);
- audio_bytes += audio_bytes % 4;
- this->AudioSeek(audio_bytes);
- ReleaseMutex(this->hIOMutex);
- return 1;
- }
- return 1;
- }
- int AviDecaps::ReSeekAudio()
- {
- double ratio;
- long audio_bytes;
- if(this->AudioStreams() > 0) {
- WaitForSingleObject(this->hIOMutex, INFINITE);
- ratio = (double) ((double) this->video_pos / (double) this->video_frames);
-
- /*
- * and set audio
- * position
- *
- */
- audio_bytes = (long) (ratio * this->audio_bytes);
- audio_bytes += audio_bytes % 4;
- this->AudioSeek(audio_bytes);
- ReleaseMutex(this->hIOMutex);
- }
- return 1;
- }
- int AviDecaps::Rewind()
- {
- this->video_pos = 0;
- this->audio_posc = 0;
- this->audio_posb = 0;
- return 1;
- }
- double AviDecaps::GetProgress()
- {
- return (double) ((double)(this->video_pos))*100.0/((double)this->video_frames);
- }
- int AviDecaps::GetBufferingState() {
- if(this->input != NULL) {
- return this->input->getBufferState();
- }
- return 0;
- }
- int AviDecaps::Close()
- {
- if(this->input) {
- this->input->Close();
-
- delete this->input;
- this->input = NULL;
- }
- if(this->idx)
- free(this->idx);
- if(this->video_index)
- free(this->video_index);
-
- if(this->audio_index)
- free(this->audio_index);
-
- CloseHandle(this->hIOMutex);
- return 1;
- }