105 lines
2.2 KiB
PHP
105 lines
2.2 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Base class for streaming media segment readers
|
||
|
*
|
||
|
* @file
|
||
|
* @ingroup HLS
|
||
|
*/
|
||
|
|
||
|
namespace MediaWiki\TimedMediaHandler\HLS;
|
||
|
|
||
|
use Exception;
|
||
|
|
||
|
/**
|
||
|
* Base class for reading/writing a media file with wrappers
|
||
|
* for exception handling and possible multi usage.
|
||
|
*/
|
||
|
class StreamReader {
|
||
|
/**
|
||
|
* @var resource
|
||
|
*/
|
||
|
protected $file;
|
||
|
|
||
|
protected int $pos;
|
||
|
|
||
|
/**
|
||
|
* @param resource $file
|
||
|
*/
|
||
|
public function __construct( $file ) {
|
||
|
if ( get_resource_type( $file ) !== 'stream' ) {
|
||
|
throw new Exception( 'Invalid file stream' );
|
||
|
}
|
||
|
$this->file = $file;
|
||
|
$this->pos = $this->tell();
|
||
|
}
|
||
|
|
||
|
private function tell(): int {
|
||
|
return ftell( $this->file );
|
||
|
}
|
||
|
|
||
|
public function pos(): int {
|
||
|
return $this->pos;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Seek to given absolute file position.
|
||
|
* @throws Exception on error
|
||
|
*/
|
||
|
public function seek( int $pos ): void {
|
||
|
$this->pos = intval( $pos );
|
||
|
|
||
|
if ( $this->pos === $this->tell() ) {
|
||
|
return;
|
||
|
}
|
||
|
$retval = fseek( $this->file, $this->pos, SEEK_SET );
|
||
|
|
||
|
if ( $retval < 0 ) {
|
||
|
throw new Exception( "Failed to seek to $this->pos bytes" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read $len bytes or return null on EOF/short read.
|
||
|
* @throws Exception on error
|
||
|
*/
|
||
|
public function read( int $len ): ?string {
|
||
|
$this->seek( $this->pos );
|
||
|
$bytes = fread( $this->file, $len );
|
||
|
if ( $bytes === false ) {
|
||
|
throw new Exception( "Read error for $len bytes at $this->pos" );
|
||
|
}
|
||
|
if ( strlen( $bytes ) < $len ) {
|
||
|
return null;
|
||
|
}
|
||
|
$this->pos += strlen( $bytes );
|
||
|
return $bytes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read exactly $len bytes
|
||
|
* @throws Exception on error or short read
|
||
|
*/
|
||
|
public function readExactly( int $len ): string {
|
||
|
$bytes = $this->read( $len );
|
||
|
if ( $bytes === null ) {
|
||
|
throw new Exception( "Short read for $len bytes at $this->pos" );
|
||
|
}
|
||
|
return $bytes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write the given data and return number of bytes written.
|
||
|
* Short writes are possible on network connections, in theory.
|
||
|
* @throws Exception on error
|
||
|
*/
|
||
|
public function write( string $bytes ): int {
|
||
|
$this->seek( $this->pos );
|
||
|
$len = strlen( $bytes );
|
||
|
$nbytes = fwrite( $this->file, $bytes );
|
||
|
if ( $nbytes === false ) {
|
||
|
throw new Exception( "Write error for $len bytes at $this->pos" );
|
||
|
}
|
||
|
return $nbytes;
|
||
|
}
|
||
|
}
|