/* SMPEG - SDL MPEG Player Library Copyright (C) 1999 Loki Entertainment Software This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* The generic MPEG stream class */ #include #include "MPEG.h" #include "MPEGstream.h" #include "video/video.h" /* This is the limit of the quantity of pre-read data */ #define MAX_QUEUE (256 * 1024) MPEGstream::MPEGstream(MPEGsystem * System, Uint8 Streamid) { system = System; streamid = Streamid; br = new MPEGlist(); cleareof = true; data = 0; stop = 0; pos = 0; preread_size = 0; enabled = true; mutex = SDL_CreateMutex(); } MPEGstream::~MPEGstream() { MPEGlist * newbr; SDL_DestroyMutex(mutex); /* Free the list */ for(newbr = br; newbr->Prev(); newbr = newbr->Prev()); while(newbr->Next()) { newbr = newbr->Next(); delete newbr->Prev(); } delete newbr; } void MPEGstream::reset_stream() { MPEGlist * newbr; SDL_mutexP(mutex); /* Seek the first buffer */ for(newbr = br; newbr->Prev(); newbr = newbr->Prev()); /* Free buffers */ while(newbr->Next()) { newbr = newbr->Next(); delete newbr->Prev(); } delete newbr; br = new MPEGlist(); cleareof = true; data = 0; stop = 0; pos = 0; preread_size = 0; SDL_mutexV(mutex); } void MPEGstream::rewind_stream() { /* Note that this will rewind all streams, and other streams than this one */ /* will finish reading their prebuffured data (they are not reseted) */ /* This should works because there are always sequence start codes or */ /* audio start codes at the beginning of the streams */ /* Of course, this won't work on network streams */ /* Restart the system */ system->Rewind(); } bool MPEGstream:: next_system_buffer(void) { bool has_data = true; /* No more buffer ? */ while(has_data && !br->Next()) { SDL_mutexV(mutex); system->RequestBuffer(); has_data = system->Wait(); SDL_mutexP(mutex); } if ( has_data && (br->Size() || cleareof) ) { cleareof = false; br = br->Next(); preread_size -= br->Size(); } return(has_data); } bool MPEGstream:: next_packet(bool recurse, bool update_timestamp) { SDL_mutexP(mutex); /* Unlock current buffer */ br->Unlock(); /* Check for the end of stream mark */ next_system_buffer(); if(eof()) { /* Report eof */ SDL_mutexV(mutex); return(false); } /* Lock the buffer */ br->Lock(); /* Make sure that we have read buffers in advance if possible */ if(preread_size < MAX_QUEUE) system->RequestBuffer(); /* Update stream datas */ data = (Uint8 *) br->Buffer(); stop = data + br->Size(); if(update_timestamp){ timestamp = br->TimeStamp; timestamp_pos = pos; } SDL_mutexV(mutex); return(true); } MPEGstream_marker * MPEGstream:: new_marker(int offset) { MPEGstream_marker * marker; SDL_mutexP(mutex); /* We can't mark past the end of the stream */ if ( eof() ) { SDL_mutexV(mutex); return(0); } /* It may be possible to seek in the data stream, but punt for now */ if ( ((data+offset) < br->Buffer()) || ((data+offset) > stop) ) { SDL_mutexV(mutex); return(0); } /* Set up the mark */ marker = new MPEGstream_marker; marker->marked_buffer = br; marker->marked_data = data+offset; marker->marked_stop = stop; /* Lock the new buffer */ marker->marked_buffer->Lock(); SDL_mutexV(mutex); return(marker); } bool MPEGstream:: seek_marker(MPEGstream_marker const * marker) { SDL_mutexP(mutex); if ( marker ) { /* Release current buffer */ if(br->IsLocked()) { br->Unlock(); marker->marked_buffer->Lock(); } /* Reset the data positions */ br = marker->marked_buffer; data = marker->marked_data; stop = marker->marked_stop; } SDL_mutexV(mutex); return(marker != 0); } void MPEGstream:: delete_marker(MPEGstream_marker *marker) { if( marker && marker->marked_buffer) { marker->marked_buffer->Unlock(); delete marker; } } Uint32 MPEGstream:: copy_data(Uint8 *area, Sint32 size, bool short_read) { Uint32 copied = 0; bool timestamped = false; while ( (size > 0) && !eof()) { Uint32 len; /* Get new data if necessary */ if ( data == stop ) { /* try to use the timestamp of the first packet */ if ( ! next_packet(true, (timestamp == -1) || !timestamped) ) { break; } timestamped = true; } SDL_mutexP(mutex); /* Copy as much as we need */ if ( size <= (Sint32)(stop-data) ) { len = size; } else { len = (stop-data); } memcpy(area, data, len); area += len; data += len; size -= len; copied += len; pos += len; /* Allow 32-bit aligned short reads? */ if ( ((copied%4) == 0) && short_read ) { break; } SDL_mutexV(mutex); } return(copied); } int MPEGstream::copy_byte(void) { /* Get new data if necessary */ if ( data == stop ) { if ( ! next_packet() ) { return (-1); } } pos ++; return(*data++); } bool MPEGstream::eof() const { return(!br->Size()); } void MPEGstream::insert_packet(Uint8 * Data, Uint32 Size, double timestamp) { MPEGlist * newbr; /* Discard all packets if not enabled */ if(!enabled) return; SDL_mutexP(mutex); preread_size += Size; /* Seek the last buffer */ for(newbr = br; newbr->Next(); newbr = newbr->Next()); /* Position ourselves at the end of the stream */ newbr = newbr->Alloc(Size); if ( Size ) { memcpy(newbr->Buffer(), Data, Size); } newbr->TimeStamp = timestamp; SDL_mutexV(mutex); garbage_collect(); } /* - Check for unused buffers and free them - */ void MPEGstream::garbage_collect(void) { MPEGlist * newbr; SDL_mutexP(mutex); br->Lock(); /* First of all seek the first buffer */ for(newbr = br; newbr->Prev(); newbr = newbr->Prev()); /* Now free buffers until we find a locked buffer */ while(newbr->Next() && !newbr->IsLocked()) { newbr = newbr->Next(); delete newbr->Prev(); } br->Unlock(); SDL_mutexV(mutex); } void MPEGstream::enable(bool toggle) { enabled = toggle; } double MPEGstream::time() { return(br->TimeStamp); }