scratch – Blame information for rev

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) Strime <contact@strime.io>
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 Alchemy\BinaryDriver\Exception\InvalidArgumentException;
16 use FFMpeg\Filters\Concat\ConcatFilterInterface;
17 use FFMpeg\Filters\Concat\ConcatFilters;
18 use FFMpeg\Driver\FFMpegDriver;
19 use FFMpeg\FFProbe;
20 use FFMpeg\Filters\Audio\SimpleFilter;
21 use FFMpeg\Exception\RuntimeException;
22 use FFMpeg\Format\FormatInterface;
23 use FFMpeg\Filters\FilterInterface;
24 use FFMpeg\Format\ProgressableInterface;
25 use FFMpeg\Format\AudioInterface;
26 use FFMpeg\Format\VideoInterface;
27 use Neutron\TemporaryFilesystem\Manager as FsManager;
28  
29 class Concat extends AbstractMediaType
30 {
31 /** @var array */
32 private $sources;
33  
34 public function __construct($sources, FFMpegDriver $driver, FFProbe $ffprobe)
35 {
36 parent::__construct($sources, $driver, $ffprobe);
37 $this->sources = $sources;
38 }
39  
40 /**
41 * Returns the path to the sources.
42 *
43 * @return string
44 */
45 public function getSources()
46 {
47 return $this->sources;
48 }
49  
50 /**
51 * {@inheritdoc}
52 *
53 * @return ConcatFilters
54 */
55 public function filters()
56 {
57 return new ConcatFilters($this);
58 }
59  
60 /**
61 * {@inheritdoc}
62 *
63 * @return Concat
64 */
65 public function addFilter(ConcatFilterInterface $filter)
66 {
67 $this->filters->add($filter);
68  
69 return $this;
70 }
71  
72 /**
73 * Saves the concatenated video in the given array, considering that the sources videos are all encoded with the same codec.
74 *
75 * @param array $outputPathfile
76 * @param string $streamCopy
77 *
78 * @return Concat
79 *
80 * @throws RuntimeException
81 */
82 public function saveFromSameCodecs($outputPathfile, $streamCopy = TRUE)
83 {
84 /**
85 * @see https://ffmpeg.org/ffmpeg-formats.html#concat
86 * @see https://trac.ffmpeg.org/wiki/Concatenate
87 */
88  
89 // Create the file which will contain the list of videos
90 $fs = FsManager::create();
91 $sourcesFile = $fs->createTemporaryFile('ffmpeg-concat');
92  
93 // Set the content of this file
94 $fileStream = @fopen($sourcesFile, 'w');
95  
96 if($fileStream === false) {
97 throw new ExecutionFailureException('Cannot open the temporary file.');
98 }
99  
100 $count_videos = 0;
101 if(is_array($this->sources) && (count($this->sources) > 0)) {
102 foreach ($this->sources as $videoPath) {
103 $line = "";
104  
105 if($count_videos != 0)
106 $line .= "\n";
107  
108 $line .= "file ".$videoPath;
109  
110 fwrite($fileStream, $line);
111  
112 $count_videos++;
113 }
114 }
115 else {
116 throw new InvalidArgumentException('The list of videos is not a valid array.');
117 }
118 fclose($fileStream);
119  
120  
121 $commands = array(
122 '-f', 'concat', '-safe', '0',
123 '-i', $sourcesFile
124 );
125  
126 // Check if stream copy is activated
127 if($streamCopy === TRUE) {
128 $commands[] = '-c';
129 $commands[] = 'copy';
130 }
131  
132 // If not, we can apply filters
133 else {
134 foreach ($this->filters as $filter) {
135 $commands = array_merge($commands, $filter->apply($this));
136 }
137 }
138  
139 // Set the output file in the command
140 $commands = array_merge($commands, array($outputPathfile));
141  
142 // Execute the command
143 try {
144 $this->driver->command($commands);
145 } catch (ExecutionFailureException $e) {
146 $this->cleanupTemporaryFile($outputPathfile);
147 $this->cleanupTemporaryFile($sourcesFile);
148 throw new RuntimeException('Unable to save concatenated video', $e->getCode(), $e);
149 }
150  
151 $this->cleanupTemporaryFile($sourcesFile);
152 return $this;
153 }
154  
155 /**
156 * Saves the concatenated video in the given filename, considering that the sources videos are all encoded with the same codec.
157 *
158 * @param string $outputPathfile
159 *
160 * @return Concat
161 *
162 * @throws RuntimeException
163 */
164 public function saveFromDifferentCodecs(FormatInterface $format, $outputPathfile)
165 {
166 /**
167 * @see https://ffmpeg.org/ffmpeg-formats.html#concat
168 * @see https://trac.ffmpeg.org/wiki/Concatenate
169 */
170  
171 // Check the validity of the parameter
172 if(!is_array($this->sources) || (count($this->sources) == 0)) {
173 throw new InvalidArgumentException('The list of videos is not a valid array.');
174 }
175  
176 // Create the commands variable
177 $commands = array();
178  
179 // Prepare the parameters
180 $nbSources = 0;
181 $files = array();
182  
183 // For each source, check if this is a legit file
184 // and prepare the parameters
185 foreach ($this->sources as $videoPath) {
186 $files[] = '-i';
187 $files[] = $videoPath;
188 $nbSources++;
189 }
190  
191 $commands = array_merge($commands, $files);
192  
193 // Set the parameters of the request
194 $commands[] = '-filter_complex';
195  
196 $complex_filter = '';
197 for($i=0; $i<$nbSources; $i++) {
198 $complex_filter .= '['.$i.':v:0] ['.$i.':a:0] ';
199 }
200 $complex_filter .= 'concat=n='.$nbSources.':v=1:a=1 [v] [a]';
201  
202 $commands[] = $complex_filter;
203 $commands[] = '-map';
204 $commands[] = '[v]';
205 $commands[] = '-map';
206 $commands[] = '[a]';
207  
208 // Prepare the filters
209 $filters = clone $this->filters;
210 $filters->add(new SimpleFilter($format->getExtraParams(), 10));
211  
212 if ($this->driver->getConfiguration()->has('ffmpeg.threads')) {
213 $filters->add(new SimpleFilter(array('-threads', $this->driver->getConfiguration()->get('ffmpeg.threads'))));
214 }
215 if ($format instanceof VideoInterface) {
216 if (null !== $format->getVideoCodec()) {
217 $filters->add(new SimpleFilter(array('-vcodec', $format->getVideoCodec())));
218 }
219 }
220 if ($format instanceof AudioInterface) {
221 if (null !== $format->getAudioCodec()) {
222 $filters->add(new SimpleFilter(array('-acodec', $format->getAudioCodec())));
223 }
224 }
225  
226 // Add the filters
227 foreach ($this->filters as $filter) {
228 $commands = array_merge($commands, $filter->apply($this));
229 }
230  
231 if ($format instanceof AudioInterface) {
232 if (null !== $format->getAudioKiloBitrate()) {
233 $commands[] = '-b:a';
234 $commands[] = $format->getAudioKiloBitrate() . 'k';
235 }
236 if (null !== $format->getAudioChannels()) {
237 $commands[] = '-ac';
238 $commands[] = $format->getAudioChannels();
239 }
240 }
241  
242 // If the user passed some additional parameters
243 if ($format instanceof VideoInterface) {
244 if (null !== $format->getAdditionalParameters()) {
245 foreach ($format->getAdditionalParameters() as $additionalParameter) {
246 $commands[] = $additionalParameter;
247 }
248 }
249 }
250  
251 // Set the output file in the command
252 $commands[] = $outputPathfile;
253  
254 $failure = null;
255  
256 try {
257 $this->driver->command($commands);
258 } catch (ExecutionFailureException $e) {
259 throw new RuntimeException('Encoding failed', $e->getCode(), $e);
260 }
261  
262 return $this;
263 }
264 }