diff --git a/src/Mp3FileLocal.php b/src/Mp3FileLocal.php
new file mode 100644
index 0000000..0c98c54
--- /dev/null
+++ b/src/Mp3FileLocal.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace wapmorgan\Mp3Info;
+
+class Mp3FileLocal
+{
+    public string $fileName;
+
+    protected int $fileSize;
+
+    private $_filePtr;
+
+    /**
+     * Creates a new local file object.
+     *
+     * @param string $fileName URL to open
+     */
+    public function __construct(string $fileName)
+    {
+        $this->fileName = $fileName;
+        $this->_filePtr = fopen($this->fileName, 'rb');
+        $this->fileSize = filesize($this->fileName);
+    }
+
+    /**
+     * Returns the file size
+     *
+     * @return int File size
+     */
+    public function getFileSize(): int
+    {
+        return $this->fileSize;
+    }
+
+    /**
+     * Returns the given amount of Bytes from the current file position.
+     *
+     * @param int $numBytes Bytes to read
+     *
+     * @return string Read Bytes
+     */
+    public function getBytes(int $numBytes): string
+    {
+        return fread($this->_filePtr, $numBytes);
+    }
+
+    /**
+     * Returns the current file position
+     *
+     * @return int File position
+     */
+    public function getFilePos(): int
+    {
+        return ftell($this->_filePtr);
+    }
+
+    /**
+     * Sets the file point to the given position.
+     *
+     * @param int $posBytes Position to jump to
+     *
+     * @return bool TRUE if successful
+     */
+    public function seekTo(int $posBytes): bool
+    {
+        $result = fseek($this->_filePtr, $posBytes);
+        return ($result == 0);
+    }
+
+    /**
+     * Advances the file pointer the given amount.
+     *
+     * @param int $posBytes Bytes to advance
+     *
+     * @return bool TRUE if successful
+     */
+    public function seekForward(int $posBytes): bool
+    {
+        $newPos = $this->getFilePos() + $posBytes;
+        return $this->seekTo($newPos);
+    }
+
+}
diff --git a/src/Mp3FileRemote.php b/src/Mp3FileRemote.php
new file mode 100644
index 0000000..313f35f
--- /dev/null
+++ b/src/Mp3FileRemote.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace wapmorgan\Mp3Info;
+
+class Mp3FileRemote
+{
+    public string $fileName;
+    public int $blockSize;
+
+    protected $buffer;
+    protected int $filePos;
+    protected $fileSize;
+
+    /**
+     * Creates a new remote file object.
+     *
+     * @param string $fileName  URL to open
+     * @param int    $blockSize Size of the blocks to query from the server (default: 4096)
+     */
+    public function __construct(string $fileName, int $blockSize = 4096)
+    {
+        $this->fileName = $fileName;
+        $this->blockSize = $blockSize;
+        $this->buffer = [];
+        $this->filePos = 0;
+        $this->fileSize = $this->_readFileSize();
+    }
+
+    /**
+     * Returns the file size
+     *
+     * @return int File size
+     */
+    public function getFileSize(): int
+    {
+        return $this->fileSize;
+    }
+
+    /**
+     * Makes a HEAD request to get the file size
+     *
+     * @return int Content-Length header
+     */
+    private function _readFileSize(): int
+    {
+        // make HTTP HEAD request to get Content-Length
+        $context = stream_context_create([
+            'http' => [
+                'method' => 'HEAD',
+            ],
+        ]);
+        $result = get_headers($this->fileName, true, $context);
+        return $result['Content-Length'];
+    }
+
+    /**
+     * Returns the given amount of Bytes from the current file position.
+     *
+     * @param int $numBytes Bytes to read
+     *
+     * @return string Read Bytes
+     */
+    public function getBytes(int $numBytes): string
+    {
+        $blockId = intdiv($this->filePos, $this->blockSize);
+        $blockPos = $this->filePos % $this->blockSize;
+        
+        $output = [];
+        
+        do {
+            $this->downloadBlock($blockId);   // make sure we have this block
+            if ($blockPos + $numBytes >= $this->blockSize) {
+                // length of request is more than this block has, truncate to block len
+                $subLen = $this->blockSize - $blockPos;
+            } else {
+                // requested length fits inside this block
+                $subLen = $numBytes;
+            }
+            // $subLen = ($blockPos + $numBytes >= $this->blockSize) ? ($this->blockSize - $blockPos) : $numBytes;
+            $output[] = substr($this->buffer[$blockId], $blockPos, $subLen);
+            $this->filePos += $subLen;
+            $numBytes -= $subLen;
+            // advance to next block
+            $blockPos = 0;
+            $blockId++;
+        } while ($numBytes > 0);
+        
+        return implode('', $output);
+    }
+
+    /**
+     * Returns the current file position
+     *
+     * @return int File position
+     */
+    public function getFilePos(): int
+    {
+        return $this->filePos;
+    }
+
+    /**
+     * Sets the file pointer to the given position.
+     *
+     * @param int $posBytes Position to jump to
+     *
+     * @return bool TRUE if successful
+     */
+    public function seekTo(int $posBytes): bool
+    {
+        if ($posBytes < 0 || $posBytes > $this->fileSize) {
+            return false;
+        }
+        $this->filePos = $posBytes;
+        return true;
+    }
+
+    /**
+     * Advances the file pointer the given amount.
+     *
+     * @param int $posBytes Bytes to advance
+     *
+     * @return bool TRUE if successful
+     */
+    public function seekForward(int $posBytes): bool
+    {
+        $newPos = $this->filePos + $posBytes;
+        return $this->seekTo($newPos);
+    }
+
+    /**
+     * Downloads the given block if needed
+     *
+     * @param int $blockNo Block to download
+     *
+     * @return bool TRUE if successful
+     */
+    protected function downloadBlock(int $blockNo): bool
+    {
+        if (array_key_exists($blockNo, $this->buffer)) {
+            // already downloaded
+            return true;
+        }
+        $bytesFrom = $blockNo * $this->blockSize;
+        $bytesTo   = $bytesFrom + $this->blockSize - 1;
+        // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range
+        $context = stream_context_create([
+            'http' => [
+                'method' => 'GET',
+                'header' => [
+                    'Range: bytes=' . $bytesFrom . '-' . $bytesTo,
+                ],
+            ],
+        ]);
+        $filePtr = fopen($this->fileName, 'rb', false, $context);
+        $this->buffer[$blockNo] = fread($filePtr, $this->blockSize);
+        $status = stream_get_meta_data($filePtr);
+        $httpStatus = explode(' ', $status['wrapper_data'][0])[1];
+        if ($httpStatus != '206') {
+            if ($httpStatus != '200') {
+                echo 'Download error!' . PHP_EOL;
+                var_dump($status);
+                return false;
+            }
+            echo 'Server doesn\'t support partial content!' . PHP_EOL;
+            // Content received is whole file from start
+            if ($blockNo != 0) {
+                // move block to start if needed
+                $this->buffer[0] =& $this->buffer[$blockNo];
+                unset($this->buffer[$blockNo]);
+                $blockNo = 0;
+            }
+            // receive remaining parts while we're at it
+            while (!feof($filePtr)) {
+                $blockNo++;
+                $this->buffer[$blockNo] = fread($filePtr, $this->blockSize);
+            }
+        }
+        fclose($filePtr);
+        return true;
+    }
+}
diff --git a/src/Mp3Info.php b/src/Mp3Info.php
index 257b147..01d6f95 100644
--- a/src/Mp3Info.php
+++ b/src/Mp3Info.php
@@ -1,8 +1,14 @@
 <?php
+
 namespace wapmorgan\Mp3Info;
 
-use Exception;
-use RuntimeException;
+require __DIR__ . '/Mp3FileLocal.php';
+require __DIR__ . '/Mp3FileRemote.php';
+
+use \wapmorgan\Mp3Info\Mp3FileLocal;
+use \wapmorgan\Mp3Info\Mp3FileRemote;
+use \Exception;
+use \RuntimeException;
 
 /**
  * This class extracts information about an mpeg audio. (supported mpeg versions: MPEG-1, MPEG-2)
@@ -79,6 +85,11 @@ class Mp3Info {
 
     public static $framesCountRead = 2;
 
+    /**
+     * @var Mp3File File object for I/O handling
+     */
+    protected $fileObj;
+
     /**
      * @var int MPEG codec version (1 or 2 or 2.5 or undefined)
      */
@@ -208,29 +219,21 @@ class Mp3Info {
      *
      * @throws \Exception
      */
-    public function __construct($filename, $parseTags = false) {
+    public function __construct(string $filename, bool $parseTags = false)
+    {
         if (self::$_bitRateTable === null)
             self::$_bitRateTable = require dirname(__FILE__).'/../data/bitRateTable.php';
         if (self::$_sampleRateTable === null)
             self::$_sampleRateTable = require dirname(__FILE__).'/../data/sampleRateTable.php';
 
-        $this->_fileName = $filename;
-        $isLocal = (strpos($filename, '://') === false);
-        if (!$isLocal) {
-            $this->_fileSize = static::getUrlContentLength($filename);
+        if (str_contains($filename, '://')) {
+            $this->fileObj = new Mp3FileRemote($filename);
         } else {
-            if (!file_exists($filename)) {
-                throw new \Exception('File ' . $filename . ' is not present!');
-            }
-            $this->_fileSize = filesize($filename);
-        }
-
-        if ($isLocal and !static::isValidAudio($filename)) {
-            throw new \Exception('File ' . $filename . ' is not mpeg/audio!');
+            $this->fileObj = new Mp3FileLocal($filename);
         }
 
         $mode = $parseTags ? self::META | self::TAGS : self::META;
-        $this->audioSize = $this->parseAudio($this->_fileName, $this->_fileSize, $mode);
+        $this->audioSize = $this->parseAudio($mode);
     }
     
 
@@ -248,7 +251,7 @@ class Mp3Info {
             return false;
         }
         fseek($fp, $this->coverProperties['offset']);
-        $data = fread($fp, $this->coverProperties['size']);
+        $data = $this->fileObj->getBytes($this->coverProperties['size']);
         fclose($fp);
         return $data;
     }
@@ -259,44 +262,32 @@ class Mp3Info {
      * ID3V2 TAG - provides a lot of meta data. [optional]
      * MPEG AUDIO FRAMES - contains audio data. A frame consists of a frame header and a frame data. The first frame may contain extra information about mp3 (marked with "Xing" or "Info" string). Rest of frames can contain only audio data.
      * ID3V1 TAG - provides a few of meta data. [optional]
-     * @param string $filename
-     * @param int $fileSize
      * @param int $mode
      * @return float|int
      * @throws \Exception
      */
-    private function parseAudio($filename, $fileSize, $mode) {
+    private function parseAudio($mode) {
         $time = microtime(true);
 
-        // create temp storage for media
-        if (strpos($filename, '://') !== false) {
-            $fp = fopen('php://memory', 'rwb');
-            fwrite($fp, file_get_contents($filename));
-            rewind($fp);
-        } else {
-            $fp = fopen($filename, 'rb');
-        }
-
         /** @var int Size of audio data (exclude tags size) */
-        $audioSize = $fileSize;
+        $audioSize = $this->fileObj->getFileSize();
 
         // parse tags
-        if (fread($fp, 3) == self::TAG2_SYNC) {
-            if ($mode & self::TAGS) $audioSize -= ($this->_id3Size = $this->readId3v2Body($fp));
-            else {
-                fseek($fp, 2, SEEK_CUR); // 2 bytes of tag version
-                fseek($fp, 1, SEEK_CUR); // 1 byte of tag flags
-                $sizeBytes = $this->readBytes($fp, 4);
-                array_walk($sizeBytes, function (&$value) {
-                    $value = substr(str_pad(base_convert($value, 10, 2), 8, 0, STR_PAD_LEFT), 1);
-                });
-                $size = bindec(implode($sizeBytes)) + 10;
+        if ($this->fileObj->getBytes(3) == self::TAG2_SYNC) {
+            if ($mode & self::TAGS) {
+                $audioSize -= ($this->_id3Size = $this->readId3v2Body());
+            } else {
+                $this->fileObj->seekForward(2); // 2 bytes of tag version
+                $this->fileObj->seekForward(1); // 1 byte of tag flags
+                $sizeBytes = unpack('C4', $this->fileObj->getBytes(4));
+                $size = $sizeBytes[1] << 21 | $sizeBytes[2] << 14 | $sizeBytes[3] << 7 | $sizeBytes[4];
+                $size += 10;   // add header size
                 $audioSize -= ($this->_id3Size = $size);
             }
         }
 
-        fseek($fp, $fileSize - 128);
-        if (fread($fp, 3) == self::TAG1_SYNC) {
+        $this->fileObj->seekTo($this->fileObj->getFileSize() - 128);
+        if ($this->fileObj->getBytes(3) == self::TAG1_SYNC) {
             if ($mode & self::TAGS) $audioSize -= $this->readId3v1Body($fp);
             else $audioSize -= 128;
         }
@@ -305,17 +296,17 @@ class Mp3Info {
             $this->fillTags();
         }
 
-        fseek($fp, 0);
+        $this->fileObj->seekTo(0);
         // audio meta
         if ($mode & self::META) {
-            if ($this->_id3Size !== null) fseek($fp, $this->_id3Size);
+            if ($this->_id3Size !== null) $this->fileObj->seekTo($this->_id3Size);
             /**
              * First frame can lie. Need to fix in the future.
              * @link https://github.com/wapmorgan/Mp3Info/issues/13#issuecomment-447470813
              * Read first N frames
              */
             for ($i = 0; $i < self::$framesCountRead; $i++) {
-                $framesCount = $this->readMpegFrame($fp);
+                $framesCount = $this->readMpegFrame();
             }
 
             $this->_framesCount = $framesCount !== null
@@ -338,7 +329,6 @@ class Mp3Info {
             // Calculate total number of audio samples (framesCount * sampleInFrameCount) / samplesInSecondCount
             $this->duration = ($this->_framesCount - 1) * $samples_in_second / $this->sampleRate;
         }
-        fclose($fp);
 
         $this->_parsingTime = microtime(true) - $time;
         return $audioSize;
@@ -346,48 +336,52 @@ class Mp3Info {
 
     /**
      * Read first frame information.
-     * @param resource $fp
      * @return int Number of frames (if present if first frame of VBR-file)
      * @throws \Exception
      */
-    private function readMpegFrame($fp) {
-        $header_seek_pos = ftell($fp) + self::$headerSeekLimit;
+    private function readMpegFrame() {
+        $header_seek_pos = $this->fileObj->getFilePos() + self::$headerSeekLimit;
         do {
-            $pos = ftell($fp);
-            $first_header_byte = $this->readBytes($fp, 1);
-            if ($first_header_byte[0] === 0xFF) {
-                $second_header_byte = $this->readBytes($fp, 1);
-                if ((($second_header_byte[0] >> 5) & 0b111) == 0b111) {
-                    fseek($fp, $pos);
-                    $header_bytes = $this->readBytes($fp, 4);
+            $pos = $this->fileObj->getFilePos();
+            $first_header_byte = $this->fileObj->getBytes(1);
+            if (ord($first_header_byte[0]) === 0xFF) {
+                $second_header_byte = $this->fileObj->getBytes(1);
+                if (((ord($second_header_byte[0]) >> 5) & 0b111) == 0b111) {
+                    $this->fileObj->seekTo($pos);
+                    $header_bytes = $this->fileObj->getBytes(4);
                     break;
+                } else {
+                    $this->fileObj->seekForward(-1);
                 }
+            } else {
+                $this->fileObj->seekForward(-1);
             }
-            fseek($fp, 1, SEEK_CUR);
-        } while (ftell($fp) <= $header_seek_pos);
+            $this->fileObj->seekForward(1);
+        } while ($this->fileObj->getFilePos() <= $header_seek_pos);
 
-        if (!isset($header_bytes) || $header_bytes[0] !== 0xFF || (($header_bytes[1] >> 5) & 0b111) != 0b111) {
+        if (!isset($header_bytes) || ord($header_bytes[0]) !== 0xFF || ((ord($header_bytes[1]) >> 5) & 0b111) != 0b111) {
             throw new \Exception('At '.$pos
                 .'(0x'.dechex($pos).') should be a frame header!');
         }
 
-        switch ($header_bytes[1] >> 3 & 0b11) {
+        switch (ord($header_bytes[1]) >> 3 & 0b11) {
             case 0b00: $this->codecVersion = self::MPEG_25; break;
-            case 0b01: $this->codecVersion = self::CODEC_UNDEFINED; break;
+            case 0b01: return null; break;
             case 0b10: $this->codecVersion = self::MPEG_2; break;
             case 0b11: $this->codecVersion = self::MPEG_1; break;
         }
 
-        switch ($header_bytes[1] >> 1 & 0b11) {
+        switch (ord($header_bytes[1]) >> 1 & 0b11) {
             case 0b01: $this->layerVersion = self::LAYER_3; break;
             case 0b10: $this->layerVersion = self::LAYER_2; break;
             case 0b11: $this->layerVersion = self::LAYER_1; break;
         }
 
-        $this->bitRate = self::$_bitRateTable[$this->codecVersion][$this->layerVersion][$header_bytes[2] >> 4];
-        $this->sampleRate = self::$_sampleRateTable[$this->codecVersion][($header_bytes[2] >> 2) & 0b11];
+        $this->bitRate = self::$_bitRateTable[$this->codecVersion][$this->layerVersion][ord($header_bytes[2]) >> 4];
+        $this->sampleRate = self::$_sampleRateTable[$this->codecVersion][(ord($header_bytes[2]) >> 2) & 0b11];
+        if ($this->sampleRate === false) return null;
 
-        switch ($header_bytes[3] >> 6) {
+        switch (ord($header_bytes[3]) >> 6) {
             case 0b00: $this->channel = self::STEREO; break;
             case 0b01: $this->channel = self::JOINT_STEREO; break;
             case 0b10: $this->channel = self::DUAL_MONO; break;
@@ -397,69 +391,54 @@ class Mp3Info {
         $vbr_offset = self::$_vbrOffsets[$this->codecVersion][$this->channel == self::MONO ? 0 : 1];
 
         // check for VBR
-        fseek($fp, $pos + $vbr_offset);
-        if (fread($fp, 4) == self::VBR_SYNC) {
+        $this->fileObj->seekTo($pos + $vbr_offset);
+        if ($this->fileObj->getBytes(4) == self::VBR_SYNC) {
             $this->isVbr = true;
-            $flagsBytes = $this->readBytes($fp, 4);
+            $flagsBytes = $this->fileObj->getBytes(4);
 
             // VBR frames count presence
-            if (($flagsBytes[3] & 2)) {
-                $this->vbrProperties['frames'] = implode(unpack('N', fread($fp, 4)));
+            if ((ord($flagsBytes[3]) & 2)) {
+                $this->vbrProperties['frames'] = implode(unpack('N', $this->fileObj->getBytes(4)));
             }
             // VBR stream size presence
-            if ($flagsBytes[3] & 4) {
-                $this->vbrProperties['bytes'] = implode(unpack('N', fread($fp, 4)));
+            if (ord($flagsBytes[3]) & 4) {
+                $this->vbrProperties['bytes'] = implode(unpack('N', $this->fileObj->getBytes(4)));
             }
             // VBR TOC presence
-            if ($flagsBytes[3] & 1) {
-                fseek($fp, 100, SEEK_CUR);
+            if (ord($flagsBytes[3]) & 1) {
+                $this->fileObj->seekForward(100);
             }
             // VBR quality
-            if ($flagsBytes[3] & 8) {
-                $this->vbrProperties['quality'] = implode(unpack('N', fread($fp, 4)));
+            if (ord($flagsBytes[3]) & 8) {
+                $this->vbrProperties['quality'] = implode(unpack('N', $this->fileObj->getBytes(4)));
             }
         }
 
         // go to the end of frame
         if ($this->layerVersion == self::LAYER_1) {
-            $this->_cbrFrameSize = floor((12 * $this->bitRate / $this->sampleRate + ($header_bytes[2] >> 1 & 0b1)) * 4);
+            $this->_cbrFrameSize = floor((12 * $this->bitRate / $this->sampleRate + (ord($header_bytes[2]) >> 1 & 0b1)) * 4);
         } else {
-            $this->_cbrFrameSize = floor(144 * $this->bitRate / $this->sampleRate + ($header_bytes[2] >> 1 & 0b1));
+            $this->_cbrFrameSize = floor(144 * $this->bitRate / $this->sampleRate + (ord($header_bytes[2]) >> 1 & 0b1));
         }
 
-        fseek($fp, $pos + $this->_cbrFrameSize);
+        $this->fileObj->seekTo($pos + $this->_cbrFrameSize);
 
         return isset($this->vbrProperties['frames']) ? $this->vbrProperties['frames'] : null;
     }
 
-    /**
-     * @param $fp
-     * @param $n
-     *
-     * @return array
-     * @throws \Exception
-     */
-    private function readBytes($fp, $n) {
-        $raw = fread($fp, $n);
-        if (strlen($raw) !== $n) throw new \Exception('Unexpected end of file!');
-        $bytes = array();
-        for($i = 0; $i < $n; $i++) $bytes[$i] = ord($raw[$i]);
-        return $bytes;
-    }
-
     /**
      * Reads id3v1 tag.
      * @return int Returns length of id3v1 tag.
      */
-    private function readId3v1Body($fp) {
-        $this->tags1['song'] = trim(fread($fp, 30));
-        $this->tags1['artist'] = trim(fread($fp, 30));
-        $this->tags1['album'] = trim(fread($fp, 30));
-        $this->tags1['year'] = trim(fread($fp, 4));
-        $this->tags1['comment'] = trim(fread($fp, 28));
-        fseek($fp, 1, SEEK_CUR);
-        $this->tags1['track'] = ord(fread($fp, 1));
-        $this->tags1['genre'] = ord(fread($fp, 1));
+    private function readId3v1Body() {
+        $this->tags1['song'] = trim($this->fileObj->getBytes(30));
+        $this->tags1['artist'] = trim($this->fileObj->getBytes(30));
+        $this->tags1['album'] = trim($this->fileObj->getBytes(30));
+        $this->tags1['year'] = trim($this->fileObj->getBytes(4));
+        $this->tags1['comment'] = trim($this->fileObj->getBytes(28));
+        $this->fileObj->seekForward(1);
+        $this->tags1['track'] = ord($this->fileObj->getBytes(1));
+        $this->tags1['genre'] = ord($this->fileObj->getBytes(1));
         return 128;
     }
 
@@ -512,10 +491,10 @@ class Mp3Info {
      * @return int Returns length of id3v2 tag.
      * @throws \Exception
      */
-    private function readId3v2Body($fp)
+    private function readId3v2Body()
     {
         // read the rest of the id3v2 header
-        $raw = fread($fp, 7);
+        $raw = $this->fileObj->getBytes(7);
         $data = unpack('cmajor_version/cminor_version/H*', $raw);
         $this->id3v2MajorVersion = $data['major_version'];
         $this->id3v2MinorVersion = $data['minor_version'];
@@ -562,10 +541,10 @@ class Mp3Info {
             /*throw new \Exception('NEED TO PARSE id3v2.2.0 flags!');*/
         } else if ($this->id3v2MajorVersion == 3) {
             // parse id3v2.3.0 body
-            $this->parseId3v23Body($fp, 10 + $size);
+            $this->parseId3v23Body(10 + $size);
         } else if ($this->id3v2MajorVersion == 4)  {
             // parse id3v2.4.0 body
-            $this->parseId3v24Body($fp, 10 + $size);
+            $this->parseId3v24Body(10 + $size);
         }
 
         return 10 + $size; // 10 bytes - header, rest - body
@@ -575,9 +554,9 @@ class Mp3Info {
      * Parses id3v2.3.0 tag body.
      * @todo Complete.
      */
-    protected function parseId3v23Body($fp, $lastByte) {
-        while (ftell($fp) < $lastByte) {
-            $raw = fread($fp, 10);
+    protected function parseId3v23Body($lastByte) {
+        while ($this->fileObj->getFilePos() < $lastByte) {
+            $raw = $this->fileObj->getBytes(10);
             $frame_id = substr($raw, 0, 4);
 
             if ($frame_id == str_repeat(chr(0), 4)) {
@@ -643,7 +622,7 @@ class Mp3Info {
                 case 'TSIZ':    # Size
                 case 'TSRC':    # ISRC (international standard recording code)
                 case 'TSSE':    # Software/Hardware and settings used for encoding
-                    $this->tags2[$frame_id] = $this->handleTextFrame($frame_size, fread($fp, $frame_size));
+                    $this->tags2[$frame_id] = $this->handleTextFrame($frame_size, $this->fileObj->getBytes($frame_size));
                     break;
                 ################# Text information frames
 
@@ -683,15 +662,15 @@ class Mp3Info {
                 // case 'SYLT':    # Synchronized lyric/text
                 //     break;
                 case 'COMM':    # Comments
-                    $dataEnd = ftell($fp) + $frame_size;
-                    $raw = fread($fp, 4);
+                    $dataEnd = $this->fileObj->getFilePos() + $frame_size;
+                    $raw = $this->fileObj->getBytes(4);
                     $data = unpack('C1encoding/A3language', $raw);
                     // read until \null character
                     $short_description = '';
                     $last_null = false;
                     $actual_text = false;
-                    while (ftell($fp) < $dataEnd) {
-                        $char = fgetc($fp);
+                    while ($this->fileObj->getFilePos() < $dataEnd) {
+                        $char = $this->fileObj->getBytes(1);
                         if ($char == "\00" && $actual_text === false) {
                             if ($data['encoding'] == 0x1) { # two null-bytes for utf-16
                                 if ($last_null)
@@ -721,20 +700,20 @@ class Mp3Info {
                 //     break;
                  case 'APIC':    # Attached picture
                      $this->hasCover = true;
-                     $last_byte = ftell($fp) + $frame_size;
-                     $this->coverProperties = ['text_encoding' => ord(fread($fp, 1))];
+                     $last_byte = $this->fileObj->getFilePos() + $frame_size;
+                     $this->coverProperties = ['text_encoding' => ord($this->fileObj->getBytes(1))];
 //                     fseek($fp, $frame_size - 4, SEEK_CUR);
-                     $this->coverProperties['mime_type'] = $this->readTextUntilNull($fp, $last_byte);
-                     $this->coverProperties['picture_type'] = ord(fread($fp, 1));
-                     $this->coverProperties['description'] = $this->readTextUntilNull($fp, $last_byte);
-                     $this->coverProperties['offset'] = ftell($fp);
-                     $this->coverProperties['size'] = $last_byte - ftell($fp);
-                     fseek($fp, $last_byte);
+                     $this->coverProperties['mime_type'] = $this->readTextUntilNull($last_byte);
+                     $this->coverProperties['picture_type'] = ord($this->fileObj->getBytes(1));
+                     $this->coverProperties['description'] = $this->readTextUntilNull($last_byte);
+                     $this->coverProperties['offset'] = $this->fileObj->getFilePos();
+                     $this->coverProperties['size'] = $last_byte - $this->fileObj->getFilePos();
+                     $this->fileObj->seekTo($last_byte);
                      break;
                 // case 'GEOB':    # General encapsulated object
                 //     break;
                 case 'PCNT':    # Play counter
-                    $data = unpack('L', fread($fp, $frame_size));
+                    $data = unpack('L', $this->fileObj->getBytes($frame_size));
                     $this->tags2[$frame_id] = $data[1];
                     break;
                 // case 'POPM':    # Popularimeter
@@ -760,7 +739,7 @@ class Mp3Info {
                 // case 'PRIV':    # Private frame
                 //     break;
                 default:
-                    fseek($fp, $frame_size, SEEK_CUR);
+                    $this->fileObj->seekForward($frame_size);
                     break;
             }
         }
@@ -771,14 +750,14 @@ class Mp3Info {
      * @param $fp
      * @param $lastByte
      */
-    protected function parseId3v24Body($fp, $lastByte)
+    protected function parseId3v24Body($lastByte)
     {
-        while (ftell($fp) < $lastByte) {
-            $raw = fread($fp, 10);
+        while ($this->fileObj->getFilePos() < $lastByte) {
+            $raw = $this->fileObj->getBytes(10);
             $frame_id = substr($raw, 0, 4);
 
             if ($frame_id == str_repeat(chr(0), 4)) {
-                fseek($fp, $lastByte);
+                $this->fileObj->seekTo($lastByte);
                 break;
             }
 
@@ -842,7 +821,7 @@ class Mp3Info {
                 case 'TSIZ':    # Size
                 case 'TSRC':    # ISRC (international standard recording code)
                 case 'TSSE':    # Software/Hardware and settings used for encoding
-                    $this->tags2[$frame_id] = $this->handleTextFrame($frame_size, fread($fp, $frame_size));
+                    $this->tags2[$frame_id] = $this->handleTextFrame($frame_size, $this->fileObj->getBytes($frame_size));
                     break;
 
                 ################# Text information frames
@@ -883,14 +862,14 @@ class Mp3Info {
                 // case 'SYLT':    # Synchronized lyric/text
                 //     break;
                 case 'COMM':    # Comments
-                    $dataEnd = ftell($fp) + $frame_size;
-                    $raw = fread($fp, 4);
+                    $dataEnd = $this->fileObj->getFilePos() + $frame_size;
+                    $raw = $this->fileObj->getBytes(4);
                     $data = unpack('C1encoding/A3language', $raw);
                     // read until \null character
                     $short_description = null;
                     $last_null = false;
                     $actual_text = false;
-                    while (ftell($fp) < $dataEnd) {
+                    while ($this->fileObj->getFilePos() < $dataEnd) {
                         $char = fgetc($fp);
                         if ($char == "\00" && $actual_text === false) {
                             if ($data['encoding'] == 0x1) { # two null-bytes for utf-16
@@ -921,20 +900,20 @@ class Mp3Info {
                 //     break;
                 case 'APIC':    # Attached picture
                     $this->hasCover = true;
-                    $last_byte = ftell($fp) + $frame_size;
-                    $this->coverProperties = ['text_encoding' => ord(fread($fp, 1))];
-//                     fseek($fp, $frame_size - 4, SEEK_CUR);
+                    $last_byte = $this->fileObj->getFilePos() + $frame_size;
+                    $this->coverProperties = ['text_encoding' => ord($this->fileObj->getBytes(1))];
+//                     $this->fileObj->seekForward($frame_size - 4);
                     $this->coverProperties['mime_type'] = $this->readTextUntilNull($fp, $last_byte);
-                    $this->coverProperties['picture_type'] = ord(fread($fp, 1));
+                    $this->coverProperties['picture_type'] = ord($this->fileObj->getBytes(1));
                     $this->coverProperties['description'] = $this->readTextUntilNull($fp, $last_byte);
-                    $this->coverProperties['offset'] = ftell($fp);
-                    $this->coverProperties['size'] = $last_byte - ftell($fp);
-                    fseek($fp, $last_byte);
+                    $this->coverProperties['offset'] = $this->fileObj->getFilePos();
+                    $this->coverProperties['size'] = $last_byte - $this->fileObj->getFilePos();
+                    $this->fileObj->seekTo($last_byte);
                     break;
                 // case 'GEOB':    # General encapsulated object
                 //     break;
                 case 'PCNT':    # Play counter
-                    $data = unpack('L', fread($fp, $frame_size));
+                    $data = unpack('L', $this->fileObj->getBytes($frame_size));
                     $this->tags2[$frame_id] = $data[1];
                     break;
                 // case 'POPM':    # Popularimeter
@@ -960,7 +939,7 @@ class Mp3Info {
                 // case 'PRIV':    # Private frame
                 //     break;
                 default:
-                    fseek($fp, $frame_size, SEEK_CUR);
+                    $this->fileObj->seekForward($frame_size);
                     break;
             }
         }
@@ -998,11 +977,11 @@ class Mp3Info {
      * @param int $dataEnd
      * @return string|null
      */
-    private function readTextUntilNull($fp, $dataEnd)
+    private function readTextUntilNull($dataEnd)
     {
         $text = null;
-        while (ftell($fp) < $dataEnd) {
-            $char = fgetc($fp);
+        while ($this->fileObj->getFilePos() < $dataEnd) {
+            $char = $this->fileObj->getBytes(1);
             if ($char === "\00") {
                 return $text;
             }
@@ -1060,22 +1039,4 @@ class Mp3Info {
             )  // id3v1 tag
             ;
     }
-
-    /**
-     * @param string $url
-     * @return int|mixed|string
-     */
-    public static function getUrlContentLength($url) {
-        $context = stream_context_create(['http' => ['method' => 'HEAD']]);
-        $head = array_change_key_case(get_headers($url, true, $context));
-        // content-length of download (in bytes), read from Content-Length: field
-        $clen = isset($head['content-length']) ? $head['content-length'] : 0;
-
-        // cannot retrieve file size, return "-1"
-        if (!$clen) {
-            return -1;
-        }
-
-        return $clen; // return size in bytes
-    }
 }