Completed
Push — master ( 7fd458...d0fdd7 )
by Iman
15s queued 11s
created

VideoStreamer::end()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 2
c 2
b 1
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Iman\Streamer;
4
5
class VideoStreamer
6
{
7
    private $path = '';
8
9
    private $stream = '';
10
11
    private $buffer = 102400;
12
13
    private $start = -1;
14
15
    private $end = -1;
16
17
    private $size = 0;
18
19
    private $mime = '';
20
21
    public function __construct($filePath, $stream)
22
    {
23
        $this->path = $filePath;
24
        $this->stream = $stream;
25
        $this->mime = mime_content_type($filePath);
26
    }
27
28
    /**
29
     * Set proper header to serve the video content.
30
     */
31
    private function setHeader()
32
    {
33
        ob_get_clean();
34
        header('Content-Type: '.$this->mime);
35
        header('Cache-Control: max-age=2592000, public');
36
        header('Expires: '.gmdate('D, d M Y H:i:s', time() + 2592000).' GMT');
37
        header('Last-Modified: '.gmdate('D, d M Y H:i:s', @filemtime($this->path)).' GMT');
38
        $this->start = 0;
39
        $this->size = filesize($this->path);
40
        $this->end = $this->size - 1;
41
        header('Accept-Ranges: 0-'.$this->end);
42
43
        if (! isset($_SERVER['HTTP_RANGE'])) {
44
            header('Content-Length: '.$this->size);
45
46
            return;
47
        }
48
49
        $c_end = $this->end;
50
        [
51
            ,
52
            $range,
53
        ] = explode('=', $_SERVER['HTTP_RANGE'], 2);
54
55
        if (strpos($range, ',') !== false) {
56
            header('HTTP/1.1 416 Requested Range Not Satisfiable');
57
            header("Content-Range: bytes $this->start-$this->end/$this->size");
58
            exit;
59
        }
60
61
        if ($range == '-') {
62
            $c_start = $this->size - substr($range, 1);
63
        } else {
64
            $range = explode('-', $range);
65
            $c_start = $range[0];
66
67
            $c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $c_end;
68
        }
69
        $c_end = ($c_end > $this->end) ? $this->end : $c_end;
70
        if ($c_start > $c_end || $c_start > $this->size - 1 || $c_end >= $this->size) {
71
            header('HTTP/1.1 416 Requested Range Not Satisfiable');
72
            header("Content-Range: bytes $this->start-$this->end/$this->size");
73
            exit;
74
        }
75
        $this->start = $c_start;
76
        $this->end = $c_end;
77
        $length = $this->end - $this->start + 1;
78
        fseek($this->stream, $this->start);
0 ignored issues
show
Bug introduced by
It seems like $this->start can also be of type string; however, parameter $offset of fseek() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

78
        fseek($this->stream, /** @scrutinizer ignore-type */ $this->start);
Loading history...
79
        header('HTTP/1.1 206 Partial Content');
80
        header('Content-Length: ' . $length);
81
        header("Content-Range: bytes $this->start-$this->end/" . $this->size);
82
    }
83
84
    /**
85
     * close curretly opened stream
86
     */
87
    private function end()
88
    {
89
        fclose($this->stream);
90
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
91
    }
92
93
    /**
94
     * perform the streaming of calculated range.
95
     */
96
    private function stream()
97
    {
98
        $i = $this->start;
99
        set_time_limit(0);
100
        while (!feof($this->stream) && $i <= $this->end) {
101
            $bytesToRead = $this->buffer;
102
            if (($i + $bytesToRead) > $this->end) {
103
                $bytesToRead = $this->end - $i + 1;
104
            }
105
            $data = fread($this->stream, $bytesToRead);
106
            echo $data;
107
            flush();
108
            $i += $bytesToRead;
109
        }
110
    }
111
112
    /**
113
     * Start streaming video content.
114
     */
115
    public function start()
116
    {
117
        $this->setHeader();
118
        $this->stream();
119
        $this->end();
120
    }
121
122
    public static function streamFile($path)
123
    {
124
        $stream = fopen($path, 'rb');
125
        if (! $stream) {
0 ignored issues
show
introduced by
$stream is of type false|resource, thus it always evaluated to false.
Loading history...
126
            throw new \Exception('File not found in: '.$path, 6542);
127
        }
128
129
        (new static($path, $stream))->start();
130
    }
131
}
132