scratch – Blame information for rev 115

Subversion Repositories:
Rev:
Rev Author Line No. Line
115 office 1 <?php
2  
3 /*
4 * This file is part of PHP-FFmpeg.
5 *
6 * (c) Alchemy <info@alchemy.fr>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11  
12 namespace FFMpeg\Media;
13  
14 use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
15 use FFMpeg\Coordinate\TimeCode;
16 use FFMpeg\Coordinate\Dimension;
17 use FFMpeg\Filters\Audio\SimpleFilter;
18 use FFMpeg\Exception\InvalidArgumentException;
19 use FFMpeg\Exception\RuntimeException;
20 use FFMpeg\Filters\Video\VideoFilters;
21 use FFMpeg\Filters\FilterInterface;
22 use FFMpeg\Format\FormatInterface;
23 use FFMpeg\Format\ProgressableInterface;
24 use FFMpeg\Format\AudioInterface;
25 use FFMpeg\Format\VideoInterface;
26 use Neutron\TemporaryFilesystem\Manager as FsManager;
27  
28 class Video extends Audio
29 {
30 /**
31 * {@inheritdoc}
32 *
33 * @return VideoFilters
34 */
35 public function filters()
36 {
37 return new VideoFilters($this);
38 }
39  
40 /**
41 * {@inheritdoc}
42 *
43 * @return Video
44 */
45 public function addFilter(FilterInterface $filter)
46 {
47 $this->filters->add($filter);
48  
49 return $this;
50 }
51  
52 /**
53 * Exports the video in the desired format, applies registered filters.
54 *
55 * @param FormatInterface $format
56 * @param string $outputPathfile
57 *
58 * @return Video
59 *
60 * @throws RuntimeException
61 */
62 public function save(FormatInterface $format, $outputPathfile)
63 {
64 $commands = array('-y', '-i', $this->pathfile);
65  
66 $filters = clone $this->filters;
67 $filters->add(new SimpleFilter($format->getExtraParams(), 10));
68  
69 if ($this->driver->getConfiguration()->has('ffmpeg.threads')) {
70 $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads'))));
71 }
72 if ($format instanceof VideoInterface) {
73 if (null !== $format->getVideoCodec()) {
74 $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec())));
75 }
76 }
77 if ($format instanceof AudioInterface) {
78 if (null !== $format->getAudioCodec()) {
79 $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec())));
80 }
81 }
82  
83 foreach ($filters as $filter) {
84 $commands = array_merge($commands, $filter->apply($this, $format));
85 }
86  
87 if ($format instanceof VideoInterface) {
88 $commands[] = '-b:v';
89 $commands[] = $format->getKiloBitrate() . 'k';
90 $commands[] = '-refs';
91 $commands[] = '6';
92 $commands[] = '-coder';
93 $commands[] = '1';
94 $commands[] = '-sc_threshold';
95 $commands[] = '40';
96 $commands[] = '-flags';
97 $commands[] = '+loop';
98 $commands[] = '-me_range';
99 $commands[] = '16';
100 $commands[] = '-subq';
101 $commands[] = '7';
102 $commands[] = '-i_qfactor';
103 $commands[] = '0.71';
104 $commands[] = '-qcomp';
105 $commands[] = '0.6';
106 $commands[] = '-qdiff';
107 $commands[] = '4';
108 $commands[] = '-trellis';
109 $commands[] = '1';
110 }
111  
112 if ($format instanceof AudioInterface) {
113 if (null !== $format->getAudioKiloBitrate()) {
114 $commands[] = '-b:a';
115 $commands[] = $format->getAudioKiloBitrate() . 'k';
116 }
117 if (null !== $format->getAudioChannels()) {
118 $commands[] = '-ac';
119 $commands[] = $format->getAudioChannels();
120 }
121 }
122  
123 // If the user passed some additional parameters
124 if ($format instanceof VideoInterface) {
125 if (null !== $format->getAdditionalParameters()) {
126 foreach ($format->getAdditionalParameters() as $additionalParameter) {
127 $commands[] = $additionalParameter;
128 }
129 }
130 }
131  
132 // Merge Filters into one command
133 $videoFilterVars = $videoFilterProcesses = [];
134 for($i=0;$i<count($commands);$i++) {
135 $command = $commands[$i];
136 if ( $command == '-vf' ) {
137 $commandSplits = explode(";", $commands[$i + 1]);
138 if ( count($commandSplits) == 1 ) {
139 $commandSplit = $commandSplits[0];
140 $command = trim($commandSplit);
141 if ( preg_match("/^\[in\](.*?)\[out\]$/is", $command, $match) ) {
142 $videoFilterProcesses[] = $match[1];
143 } else {
144 $videoFilterProcesses[] = $command;
145 }
146 } else {
147 foreach($commandSplits as $commandSplit) {
148 $command = trim($commandSplit);
149 if ( preg_match("/^\[[^\]]+\](.*?)\[[^\]]+\]$/is", $command, $match) ) {
150 $videoFilterProcesses[] = $match[1];
151 } else {
152 $videoFilterVars[] = $command;
153 }
154 }
155 }
156 unset($commands[$i]);
157 unset($commands[$i + 1]);
158 $i++;
159 }
160 }
161 $videoFilterCommands = $videoFilterVars;
162 $lastInput = 'in';
163 foreach($videoFilterProcesses as $i => $process) {
164 $command = '[' . $lastInput .']';
165 $command .= $process;
166 $lastInput = 'p' . $i;
167 if ( $i == count($videoFilterProcesses) - 1 ) {
168 $command .= '[out]';
169 } else {
170 $command .= '[' . $lastInput . ']';
171 }
172  
173 $videoFilterCommands[] = $command;
174 }
175 $videoFilterCommand = implode(";", $videoFilterCommands);
176  
177 if ( $videoFilterCommand ) {
178 $commands[] = '-vf';
179 $commands[] = $videoFilterCommand;
180 }
181  
182 $fs = FsManager::create();
183 $fsId = uniqid('ffmpeg-passes');
184 $passPrefix = $fs->createTemporaryDirectory(0777, 50, $fsId) . '/' . uniqid('pass-');
185 $passes = array();
186 $totalPasses = $format->getPasses();
187  
188 if (1 > $totalPasses) {
189 throw new InvalidArgumentException('Pass number should be a positive value.');
190 }
191  
192 for ($i = 1; $i <= $totalPasses; $i++) {
193 $pass = $commands;
194  
195 if ($totalPasses > 1) {
196 $pass[] = '-pass';
197 $pass[] = $i;
198 $pass[] = '-passlogfile';
199 $pass[] = $passPrefix;
200 }
201  
202 $pass[] = $outputPathfile;
203  
204 $passes[] = $pass;
205 }
206  
207 $failure = null;
208  
209 foreach ($passes as $pass => $passCommands) {
210 try {
211 /** add listeners here */
212 $listeners = null;
213  
214 if ($format instanceof ProgressableInterface) {
215 $listeners = $format->createProgressListener($this, $this->ffprobe, $pass + 1, $totalPasses);
216 }
217  
218 $this->driver->command($passCommands, false, $listeners);
219 } catch (ExecutionFailureException $e) {
220 $failure = $e;
221 break;
222 }
223 }
224  
225 $fs->clean($fsId);
226  
227 if (null !== $failure) {
228 throw new RuntimeException('Encoding failed', $failure->getCode(), $failure);
229 }
230  
231 return $this;
232 }
233  
234 /**
235 * Gets the frame at timecode.
236 *
237 * @param TimeCode $at
238 * @return Frame
239 */
240 public function frame(TimeCode $at)
241 {
242 return new Frame($this, $this->driver, $this->ffprobe, $at);
243 }
244  
245 /**
246 * Extracts a gif from a sequence of the video.
247 *
248 * @param TimeCode $at
249 * @param Dimension $dimension
250 * @param integer $duration
251 * @return Gif
252 */
253 public function gif(TimeCode $at, Dimension $dimension, $duration = null)
254 {
255 return new Gif($this, $this->driver, $this->ffprobe, $at, $dimension, $duration);
256 }
257  
258 /**
259 * Concatenates a list of videos into one unique video.
260 *
261 * @param array $sources
262 * @return Concat
263 */
264 public function concat($sources)
265 {
266 return new Concat($sources, $this->driver, $this->ffprobe);
267 }
268 }