/*! * \file vdr_stream.c * \brief Implementation of media stream classes * * \version $Revision: 1.2 $ * \date $Date: 2004/05/28 15:29:19 $ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner * \author Responsible author: $Author: lvw $ * * $Id: vdr_stream.c,v 1.2 2004/05/28 15:29:19 lvw Exp $ * * Adapted from * MP3/MPlayer plugin to VDR (C++) * (C) 2001-2003 Stefan Huelswitt */ #include #include #include #include #include #include #include "mg_tools.h" // #include "setup-mp3.h" #include "vdr_stream.h" #include "vdr_network.h" #include "vdr_config.h" // #include "i18n.h" // #include "version.h" #define tr(x) x #ifdef USE_MMAP #include #endif #define DEFAULT_PORT 80 // default port for streaming (HTTP) using namespace std; // --- mgStream ----------------------------------------------------------------- mgStream::mgStream( string filename ) : m_filename( filename ) { m_fd = -1; m_ismmap = false; m_buffer = 0; } mgStream::~mgStream() { close(); } bool mgStream::open(bool log) { if( m_fd >= 0 ) { return seek(); } // just check, whether file exists? if( fileinfo( log ) ) { printf( "mgStream::open: fileinfo == true\n"); if( ( m_fd = ::open( m_filename.c_str(), O_RDONLY ) ) >=0 ) { printf( "mgStream::open: file opened\n" ); m_buffpos = m_readpos = 0; m_fill = 0; printf( "mgStream::open: buffpos, readpos, fill set\n" ); /* #ifdef USE_MMAP if( m_filesize <= MAX_MMAP_SIZE ) { m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ, MAP_SHARED, m_fd, 0 ); if( m_buffer != MAP_FAILED ) { m_ismmap = true; return true; } else { dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) ); } } #endif */ printf( "mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE ); m_buffer = new unsigned char[MP3FILE_BUFSIZE]; printf( "mgStream::open: buffer allocated\n" ); if( m_buffer ) { printf( "mgStream::open: buffer allocated, returning true\n" ); return true; } else { esyslog("ERROR: not enough memory for buffer: %s", m_filename.c_str() ); } } else { if( log ) { esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) ); } } } close(); printf( "mgStream::open: returning false\n" ); return false; } void mgStream::close(void) { #ifdef USE_MMAP if( m_ismmap ) { munmap( m_buffer, m_filesize ); m_buffer = 0; m_ismmap = false; } else { #endif delete m_buffer; m_buffer = 0; #ifdef USE_MMAP } #endif if( m_fd >= 0 ) { ::close( m_fd ); m_fd = -1; } } bool mgStream::seek(unsigned long long pos) { printf( "mgStream::seek\n" ); if( m_fd >= 0 && pos >= 0 && pos <= m_filesize ) { printf( "mgStream::seek valid file and position detected\n" ); m_buffpos = 0; m_fill = 0; if( m_ismmap ) { m_readpos = pos; printf( "mgStream::seek: returning true\n" ); return true; } else { if( ( m_readpos = lseek64( m_fd, pos, SEEK_SET ) ) >=0 ) { if( m_readpos != pos ) { dsyslog( "seek mismatch in %s, wanted %lld, got %lld", m_filename.c_str(), pos, m_readpos ); } printf( "mgStream::seek: returning true\n" ); return true; } else { esyslog( "ERROR: seeking failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) ); } } } else { printf( "mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos, m_filename.c_str() ); } printf( "mgStream::seek: returning false\n" ); return false; } bool mgStream::stream(unsigned char * &data, unsigned long &len, const unsigned char *rest) { if( m_fd >= 0 ) { if( m_readpos < m_filesize ) { if( m_ismmap ) { if( rest && m_fill ) { m_readpos = (rest - m_buffer); // take care of remaining data } m_fill = m_filesize - m_readpos; data = m_buffer + m_readpos; len = m_fill; m_buffpos = m_readpos; m_readpos += m_fill; return true; } else { if( rest && m_fill ) { // copy remaining data to start of buffer m_fill -= ( rest - m_buffer); // remaing bytes memmove( m_buffer, rest, m_fill ); } else { m_fill = 0; } int r; do { r = read( m_fd, m_buffer + m_fill, MP3FILE_BUFSIZE - m_fill ); } while( r == -1 && errno == EINTR ); if( r >= 0 ) { m_buffpos = m_readpos - m_fill; m_readpos += r; m_fill += r; data = m_buffer; len = m_fill; return true; } else { esyslog("ERROR: read failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) ); } } } else { len = 0; return true; } } return false; } bool mgStream::removable() { // we do not handle removable media at this time return false; } bool mgStream::fileinfo( bool log ) { struct stat64 ds; if( !stat64( m_filename.c_str(), &ds ) ) { printf( "mgStream::fileinfo: stat64 == 0\n" ); if( S_ISREG( ds.st_mode ) ) { m_fsID = ""; m_fsType = 0; struct statfs64 sfs; if( !statfs64( m_filename.c_str(), &sfs) ) { if( removable() ) { char *tmpbuf; asprintf( &tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files ); m_fsID = tmpbuf; free( tmpbuf ); } m_fsType = sfs.f_type; } else { if( errno != ENOSYS && log ) { esyslog("ERROR: can't statfs %s: %s", m_filename.c_str(), strerror(errno) ); } } m_filesize = ds.st_size; m_ctime = ds.st_ctime; #ifdef CDFS_MAGIC if( m_fsType == CDFS_MAGIC ) { m_ctime=0; // CDFS returns mount time as ctime } #endif // infodone tells that info has been read, like a cache flag // InfoDone(); return true; } else { if(log) { esyslog("ERROR: %s is not a regular file", m_filename.c_str() ); } } } else { if(log) { esyslog("ERROR: can't stat %s: %s", m_filename.c_str(), strerror(errno) ); } printf( "mgStream::fileinfo: stat64 != 0 for %s\n", m_filename.c_str() ); } return false; }