🔥 Replace all other files with 💀
This commit is contained in:
parent
001f55939b
commit
d187e02d24
124 changed files with 345 additions and 8227 deletions
13
.ac-php-conf.json
Normal file
13
.ac-php-conf.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"use-cscope": null,
|
||||
"tag-dir": null,
|
||||
"filter": {
|
||||
"php-file-ext-list": [
|
||||
"php"
|
||||
],
|
||||
"php-path-list": [
|
||||
"."
|
||||
],
|
||||
"php-path-list-without-subdir": []
|
||||
}
|
||||
}
|
||||
15
.editorconfig
Normal file
15
.editorconfig
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
22
.gitattributes
vendored
22
.gitattributes
vendored
|
|
@ -1,7 +1,15 @@
|
|||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.github export-ignore
|
||||
docs export-ignore
|
||||
tests export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
.travis.yml export-ignore
|
||||
# Path-based git attributes
|
||||
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
|
||||
|
||||
# Ignore all test and documentation with "export-ignore".
|
||||
/.github export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
/psalm.xml.dist export-ignore
|
||||
/tests export-ignore
|
||||
/.editorconfig export-ignore
|
||||
/.php-cs-fixer.dist.php export-ignore
|
||||
/art export-ignore
|
||||
/docs export-ignore
|
||||
/UPGRADING.md export-ignore
|
||||
|
|
|
|||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
github: goodevilgenius
|
||||
25
.github/ISSUE_TEMPLATE.md
vendored
25
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,25 +0,0 @@
|
|||
| Q | A
|
||||
| -------------- | ---
|
||||
| Bug? | no
|
||||
| New Feature? | no
|
||||
| Version Used | Specific tag or commit sha
|
||||
| FFmpeg Version | FFmpeg or AVConv and version
|
||||
| OS | Your OS and version
|
||||
|
||||
#### Actual Behavior
|
||||
|
||||
How does PHP-FFMpeg behave at the moment?
|
||||
|
||||
#### Expected Behavior
|
||||
|
||||
What is the behavior you expect?
|
||||
|
||||
#### Steps to Reproduce
|
||||
|
||||
What are the steps to reproduce this bug? Please add code examples,
|
||||
screenshots or links to GitHub repositories that reproduce the problem.
|
||||
|
||||
#### Possible Solutions
|
||||
|
||||
If you have already ideas how to solve the issue, add them here.
|
||||
Otherwise remove this section.
|
||||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Ask a question
|
||||
url: https://github.com/goodevilgenius/ffmpeg-mappable-media/discussions/new?category=q-a
|
||||
about: Ask the community for help
|
||||
- name: Request a feature
|
||||
url: https://github.com/goodevilgenius/ffmpeg-mappable-media/discussions/new?category=ideas
|
||||
about: Share ideas for new features
|
||||
- name: Report a security issue
|
||||
url: https://github.com/goodevilgenius/ffmpeg-mappable-media/security/policy
|
||||
about: Learn how to notify us for sensitive bugs
|
||||
- name: Report a bug
|
||||
url: https://github.com/goodevilgenius/ffmpeg-mappable-media/issues/new
|
||||
about: Report a reproducable bug
|
||||
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
36
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,36 +0,0 @@
|
|||
| Q | A
|
||||
| ------------------ | ---
|
||||
| Bug fix? | no
|
||||
| New feature? | no
|
||||
| BC breaks? | no
|
||||
| Deprecations? | no
|
||||
| Fixed tickets | fixes #issuenum
|
||||
| Related issues/PRs | #issuenum
|
||||
| License | MIT
|
||||
|
||||
#### What's in this PR?
|
||||
|
||||
Explain the contents of the PR.
|
||||
|
||||
#### Why?
|
||||
|
||||
Which problem does the PR fix?
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```php
|
||||
$foo = new Foo();
|
||||
|
||||
// Now we can do
|
||||
$foo->doSomething();
|
||||
|
||||
// Remove this section if not needed
|
||||
~~~
|
||||
|
||||
#### BC Breaks/Deprecations
|
||||
|
||||
Describe BC breaks/deprecations here (Remove this section if not needed).
|
||||
|
||||
#### To Do
|
||||
|
||||
- [ ] Create tests
|
||||
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
32
.github/workflows/dependabot-auto-merge.yml
vendored
Normal file
32
.github/workflows/dependabot-auto-merge.yml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
name: dependabot-auto-merge
|
||||
on: pull_request_target
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v1.3.3
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Auto-merge Dependabot PRs for semver-minor updates
|
||||
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Auto-merge Dependabot PRs for semver-patch updates
|
||||
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
23
.github/workflows/php-cs-fixer.yml
vendored
Normal file
23
.github/workflows/php-cs-fixer.yml
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
name: Check & fix styling
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
php-cs-fixer:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Run PHP CS Fixer
|
||||
uses: docker://oskarstark/php-cs-fixer-ga
|
||||
with:
|
||||
args: --config=.php-cs-fixer.dist.php --allow-risky=yes
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: Fix styling
|
||||
37
.github/workflows/run-tests.yml
vendored
Normal file
37
.github/workflows/run-tests.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
php: [8.0]
|
||||
stability: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
|
||||
coverage: none
|
||||
|
||||
- name: Setup problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
|
||||
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
|
||||
|
||||
- name: Execute tests
|
||||
run: vendor/bin/pest
|
||||
48
.github/workflows/test.yaml
vendored
48
.github/workflows/test.yaml
vendored
|
|
@ -1,48 +0,0 @@
|
|||
name: run-tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-20.04]
|
||||
php: [8.1, 8.0]
|
||||
ffmpeg: [5.0, 4.4]
|
||||
dependency-version: [prefer-lowest, prefer-stable]
|
||||
|
||||
name: ${{ matrix.os }} - P${{ matrix.php }} - FF${{ matrix.ffmpeg }} - ${{ matrix.dependency-version }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, mysql, mysqli, pdo_mysql, fileinfo
|
||||
coverage: none
|
||||
|
||||
- name: Install FFmpeg
|
||||
uses: Iamshankhadeep/setup-ffmpeg@ffmpeg-5.0-20220119
|
||||
with:
|
||||
version: ${{ matrix.ffmpeg }}
|
||||
id: setup-ffmpeg
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.composer/cache/files
|
||||
key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}-dep-${{ matrix.dependency-version }}
|
||||
|
||||
- name: Execute tests
|
||||
run: vendor/bin/phpunit
|
||||
env:
|
||||
FFMPEG_TEMPORARY_FILES_ROOT: ${{ github.workspace }}
|
||||
28
.github/workflows/update-changelog.yml
vendored
Normal file
28
.github/workflows/update-changelog.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
name: "Update Changelog"
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Update Changelog
|
||||
uses: stefanzweifel/changelog-updater-action@v1
|
||||
with:
|
||||
latest-version: ${{ github.event.release.name }}
|
||||
release-notes: ${{ github.event.release.body }}
|
||||
|
||||
- name: Commit updated CHANGELOG
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
branch: main
|
||||
commit_message: Update CHANGELOG
|
||||
file_pattern: CHANGELOG.md
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -1,13 +1,13 @@
|
|||
#/nbproject/
|
||||
/vendor/
|
||||
/docs/build
|
||||
composer.phar
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
.idea
|
||||
.php_cs
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
build
|
||||
composer.lock
|
||||
coverage
|
||||
docs
|
||||
phpunit.xml
|
||||
psalm.xml
|
||||
vendor
|
||||
.php-cs-fixer.cache
|
||||
/tests/Functional/output/output-*
|
||||
/docs/doctum.phar
|
||||
/docs/source/API/API/cache/twig/*
|
||||
.idea/
|
||||
/phpunit
|
||||
|
||||
|
|
|
|||
39
.php-cs-fixer.dist.php
Normal file
39
.php-cs-fixer.dist.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
$finder = Symfony\Component\Finder\Finder::create()
|
||||
->in([
|
||||
__DIR__ . '/src',
|
||||
__DIR__ . '/tests',
|
||||
])
|
||||
->name('*.php')
|
||||
->ignoreDotFiles(true)
|
||||
->ignoreVCS(true);
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'ordered_imports' => ['sort_algorithm' => 'alpha'],
|
||||
'no_unused_imports' => true,
|
||||
'not_operator_with_successor_space' => true,
|
||||
'trailing_comma_in_multiline' => true,
|
||||
'phpdoc_scalar' => true,
|
||||
'unary_operator_spaces' => true,
|
||||
'binary_operator_spaces' => true,
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
|
||||
],
|
||||
'phpdoc_single_line_var_spacing' => true,
|
||||
'phpdoc_var_without_name' => true,
|
||||
'class_attributes_separation' => [
|
||||
'elements' => [
|
||||
'method' => 'one',
|
||||
],
|
||||
],
|
||||
'method_argument_space' => [
|
||||
'on_multiline' => 'ensure_fully_multiline',
|
||||
'keep_multiple_spaces_after_comma' => true,
|
||||
],
|
||||
'single_trait_insert_per_statement' => true,
|
||||
])
|
||||
->setFinder($finder);
|
||||
173
CHANGELOG-0.x.md
173
CHANGELOG-0.x.md
|
|
@ -1,173 +0,0 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
[Unreleased]
|
||||
------------
|
||||
|
||||
### Added
|
||||
|
||||
- Add pull request and issue templates.
|
||||
- Usage of new codec "aac" of ffmpeg 3
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated changelog to follow [keepachangelog.com](http://keepachangelog.com/)
|
||||
style you see now here.
|
||||
|
||||
[0.7.0] - 2016-12-15
|
||||
--------------------
|
||||
|
||||
- Add support for FFMpeg 3 aac codec (@Nek-)
|
||||
- Add a waveform filter to extract audio waveform images (@Romain)
|
||||
|
||||
[0.6.1] - 2016-03-08
|
||||
--------------------
|
||||
|
||||
- Support PHP 7 and test against
|
||||
- Unused code cleanup (@haphan)
|
||||
- Composer and tests cleanup (PSR-4 autoloading)
|
||||
- Allow usage of evenement v2.0
|
||||
|
||||
[0.6.0] - 2016-01-30
|
||||
--------------------
|
||||
|
||||
- AbstractData::get no longer throws exceptions (@sujayjaju).
|
||||
- Add crop filter (@cangelis).
|
||||
- Fix watermark (@sujayjaju).
|
||||
|
||||
[0.5.1] - 2016-08-26
|
||||
--------------------
|
||||
|
||||
- Fix video aspect ratio calculation (@nlegoff).
|
||||
|
||||
[0.5.0] - 2014-08-12
|
||||
--------------------
|
||||
|
||||
- Add support for Wav and AAC audio formats (@MrHash).
|
||||
- Add watermark filter (@sylvainv).
|
||||
- Add configuration for audio channels (@SimonSimCity).
|
||||
|
||||
[0.4.4] - 2016-12-17
|
||||
--------------------
|
||||
|
||||
- Fix width / height dimensions extraction.
|
||||
|
||||
[0.4.3] - 2013-02-12
|
||||
--------------------
|
||||
|
||||
- Fix using rotate and resize filters at the same time (#78)
|
||||
|
||||
[0.4.2] - 2013-11-29
|
||||
--------------------
|
||||
|
||||
- Add Rotate filter.
|
||||
- Remove time_start metadata when using synchronize filter
|
||||
- Remove restriction on filesystem resources.
|
||||
|
||||
[0.4.1] - 2013-11-26
|
||||
--------------------
|
||||
|
||||
- Add Clip filter (@guimeira)
|
||||
|
||||
[0.4.0] - 2013-10-21
|
||||
--------------------
|
||||
|
||||
- Add support for video to audio transcoding
|
||||
- BC Break : Add FormatInterface::getPasses and FormatInterface::getExtraParams
|
||||
|
||||
[0.3.5] - 2013-10-21
|
||||
--------------------
|
||||
|
||||
- Add vorbis audio format (@jacobbudin).
|
||||
- Fix #66 : Allow single pass encodings.
|
||||
|
||||
[0.3.4] - 2013-09-05
|
||||
--------------------
|
||||
|
||||
- Fix Invalid ratio computing.
|
||||
|
||||
[0.3.3] - 2013-09-05
|
||||
--------------------
|
||||
|
||||
- Add convenient Stream::getDimensions method to extract video dimension.
|
||||
- Add DisplayRatioFixer Frame filter.
|
||||
|
||||
[0.3.2] - 2013-08-08
|
||||
--------------------
|
||||
|
||||
- Fix A/V synchronization over flash and HTML5 players.
|
||||
|
||||
[0.3.1] - 2013-08-06
|
||||
--------------------
|
||||
|
||||
- Allow use of FFProbe on remote URIs.
|
||||
- Fix #47 : MediaTypeInterface::save adds filters depending on the codec.
|
||||
- Save frame to target file without prompt.
|
||||
|
||||
[0.3.0] - 2013-07-04
|
||||
--------------------
|
||||
|
||||
- Complete rewrite of the library, lots of BC breaks, check the doc.
|
||||
|
||||
[0.2.4] - 2013-05-10
|
||||
--------------------
|
||||
|
||||
- Add Video\ResizableInterface::getModulus method for better output scaling (@retrojunk)
|
||||
- Fix timeout setting on audio/video encoding (@xammep-ua)
|
||||
|
||||
[0.2.3] - 2013-04-21
|
||||
--------------------
|
||||
|
||||
- Add timeout getter and setter on FFMpeg and FFProbe
|
||||
- Add timeout setting via second argument on FFMpeg::load and FFProbe::load
|
||||
|
||||
[0.2.2] - 2013-02-11
|
||||
--------------------
|
||||
|
||||
- Add compatibility with FFMpeg 1.1
|
||||
- Upgrade deprecated options (`-ab`, `-qscale` and `-b`)
|
||||
- Use of a custom stat file for each multi-pass encoding (fix #20)
|
||||
- Use larger version range for dependencies
|
||||
|
||||
[0.2.1] - 2013-02-04
|
||||
--------------------
|
||||
|
||||
- Parse the output of FFProbe using correct EOL sequences (@ak76)
|
||||
- Add process timeout customization (@pulse00)
|
||||
- Fix `accurate` option (`FFMpeg::extractImage`)
|
||||
|
||||
[0.2.0] - 2012-12-13
|
||||
--------------------
|
||||
|
||||
- Add HelperInterface and support for realtime progress ( @pulse00 ).
|
||||
- Add `accurate` option to `FFMpeg::extractImage` method.
|
||||
|
||||
0.1.0 - 2012-10-30
|
||||
--------------------
|
||||
|
||||
- First stable version.
|
||||
|
||||
[Unreleased]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.6.1...HEAD
|
||||
[0.6.1]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.6.0...0.6.1
|
||||
[0.6.0]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.5.1...0.6.0
|
||||
[0.5.1]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.5.0...0.5.1
|
||||
[0.5.0]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.4.4...0.5.0
|
||||
[0.4.4]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.4.3...0.4.4
|
||||
[0.4.3]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.4.2...0.4.3
|
||||
[0.4.2]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.4.1...0.4.2
|
||||
[0.4.1]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.4.0...0.4.1
|
||||
[0.4.0]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.3.5...0.4.0
|
||||
[0.3.5]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.3.4...0.3.5
|
||||
[0.3.4]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.3.3...0.3.4
|
||||
[0.3.3]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.3.2...0.3.3
|
||||
[0.3.2]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.3.1...0.3.2
|
||||
[0.3.1]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.3.0...0.3.1
|
||||
[0.3.0]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.2.4...0.3.0
|
||||
[0.2.4]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.2.3...0.2.4
|
||||
[0.2.3]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.2.2...0.2.3
|
||||
[0.2.2]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.2.1...0.2.2
|
||||
[0.2.1]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.2.0...0.2.1
|
||||
[0.2.0]: https://github.com/PHP-FFMpeg/PHP-FFMpeg/compare/0.1.0...0.2.0
|
||||
20
CHANGELOG.md
20
CHANGELOG.md
|
|
@ -1,22 +1,4 @@
|
|||
# Changelog
|
||||
|
||||
All Notable changes to the library will be documented in this file
|
||||
All notable changes to `ffmpeg-mappable-media` will be documented in this file.
|
||||
|
||||
## 1.0.1 - 2022-02-22
|
||||
|
||||
- Added configuration key to customize the temporary directory used for passes.
|
||||
- Fix for the path of the default `ffmpeg-passes*` temporary directory.
|
||||
|
||||
## 1.0.0 - 2022-02-09
|
||||
|
||||
Upgraded dependencies, integrated the Alchemy Binary library, and dropped support for anything below PHP 8.0
|
||||
|
||||
- Support for Symfony 5.4 and 6.0
|
||||
- Support for `psr/log` v3
|
||||
- GitHub actions against FFmpeg 4.4 and 5.0
|
||||
- Integrated the Alchemy Binary library
|
||||
- Replaced `neutron/temporary-filesystem` with `spatie/temporary-directory`
|
||||
- PHPUnit 9.5
|
||||
- Removed Silex Service Provider
|
||||
- Removed the auto-generated docs
|
||||
- Removed support for anything below PHP 8.0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012-2021 Alchemy
|
||||
Copyright (c) danjones000 <danjones@goodevilgenius.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
CHANGELOG
|
||||
---------
|
||||
* 1.6.0 (2015-03-02)
|
||||
* BC Break: bump minimum PHP versions
|
||||
* Allow use of evenement v2.0 (thanks @patkar for the P/R)
|
||||
|
||||
* 1.5.0 (2013-06-21)
|
||||
|
||||
* BC Break : ConfigurationInterface::get does not throw exceptions anymore
|
||||
in case the key does not exist. Second argument is a default value to return
|
||||
in case the key does not exist.
|
||||
|
||||
* 1.4.1 (2013-05-23)
|
||||
|
||||
* Add third parameter to BinaryInterface::command method to pass a listener or
|
||||
an array of listener that will be registered just the time of the command.
|
||||
|
||||
* 1.4.0 (2013-05-11)
|
||||
|
||||
* Extract process run management to ProcessRunner.
|
||||
* Add support for process listeners.
|
||||
* Provides bundled DebugListener.
|
||||
* Add BinaryInterface::command method.
|
||||
* BC break : ProcessRunnerInterface::run now takes an SplObjectStorage containing
|
||||
listeners as second argument.
|
||||
* BC break : BinaryInterface no longer implements LoggerAwareInterface
|
||||
as it is now supported by ProcessRunner.
|
||||
|
||||
* 1.3.4 (2013-04-26)
|
||||
|
||||
* Add BinaryDriver::run method.
|
||||
|
||||
* 1.3.3 (2013-04-26)
|
||||
|
||||
* Add BinaryDriver::createProcessMock method.
|
||||
|
||||
* 1.3.2 (2013-04-26)
|
||||
|
||||
* Add BinaryDriverTestCase for testing BinaryDriver implementations.
|
||||
|
||||
* 1.3.1 (2013-04-24)
|
||||
|
||||
* Add timeouts handling
|
||||
|
||||
* 1.3.0 (2013-04-24)
|
||||
|
||||
* Add BinaryInterface and AbstractBinary
|
||||
|
||||
* 1.2.1 (2013-04-24)
|
||||
|
||||
* Add ConfigurationAwareInterface
|
||||
* Add ProcessBuilderAwareInterface
|
||||
|
||||
* 1.2.0 (2013-04-24)
|
||||
|
||||
* Add BinaryDriver\Configuration
|
||||
|
||||
* 1.1.0 (2013-04-24)
|
||||
|
||||
* Add support for timeouts via `setTimeout` method
|
||||
|
||||
* 1.0.0 (2013-04-23)
|
||||
|
||||
* First stable version.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
BinaryDriver is released with MIT License :
|
||||
|
||||
Copyright (c) 2013 Alchemy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
# Binary Driver
|
||||
|
||||
Binary-Driver is a set of PHP tools to build binary drivers.
|
||||
|
||||
[](https://travis-ci.org/alchemy-fr/BinaryDriver)
|
||||
|
||||
## Why ?
|
||||
|
||||
You may wonder *Why building a library while I can use `exec` or
|
||||
[symfony/process](https://github.com/symfony/Process) ?*.
|
||||
|
||||
Here is a simple answer :
|
||||
|
||||
- If you use `exec`, `passthru`, `system`, `proc_open` or any low level process
|
||||
handling in PHP, you should have a look to [symfony/process](https://github.com/symfony/Process)
|
||||
component that will provide an OO portable, testable and secure interface to
|
||||
deal with this. It seems easy at first approach, but if you look at this
|
||||
component [unit tests](https://github.com/symfony/Process/tree/master/Tests),
|
||||
you will see that handling process in a simple interface can easily become a
|
||||
nightmare.
|
||||
|
||||
- If you already use symfony/process, and want to build binary drivers, you
|
||||
will always have the same common set of methods and objects to configure, log,
|
||||
debug, and generate processes.
|
||||
This library is a base to implement any binary driver with this common set of
|
||||
needs.
|
||||
|
||||
## AbstractBinary
|
||||
|
||||
`AbstractBinary` provides an abstract class to build a binary driver. It implements
|
||||
`BinaryInterface`.
|
||||
|
||||
Implementation example :
|
||||
|
||||
```php
|
||||
use Alchemy\BinaryDriver\AbstractBinary;
|
||||
|
||||
class LsDriver extends AbstractBinary
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'ls driver';
|
||||
}
|
||||
}
|
||||
|
||||
$parser = new LsParser();
|
||||
|
||||
$driver = Driver::load('ls');
|
||||
// will return the output of `ls -a -l`
|
||||
$parser->parse($driver->command(array('-a', '-l')));
|
||||
```
|
||||
|
||||
### Binary detection troubleshooting
|
||||
|
||||
If you are using Nginx with PHP-fpm, executable detection may not work because of an empty `$_ENV['path']`.
|
||||
To avoid having an empty `PATH` environment variable, add the following line to your `fastcgi_params`
|
||||
config file (replace `/your/current/path/` with the output of `printenv PATH`) :
|
||||
|
||||
```
|
||||
fastcgi_param PATH /your/current/path
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
You can log events with a `Psr\Log\LoggerInterface` by passing it in the load
|
||||
method as second argument :
|
||||
|
||||
```php
|
||||
$logger = new Monolog\Logger('driver');
|
||||
$driver = Driver::load('ls', $logger);
|
||||
```
|
||||
|
||||
## Listeners
|
||||
|
||||
You can add custom listeners on processes.
|
||||
Listeners are built on top of [Evenement](https://github.com/igorw/evenement)
|
||||
and must implement `Alchemy\BinaryDriver\ListenerInterface`.
|
||||
|
||||
```php
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class DebugListener extends EventEmitter implements ListenerInterface
|
||||
{
|
||||
public function handle($type, $data)
|
||||
{
|
||||
foreach (explode(PHP_EOL, $data) as $line) {
|
||||
$this->emit($type === Process::ERR ? 'error' : 'out', array($line));
|
||||
}
|
||||
}
|
||||
|
||||
public function forwardedEvents()
|
||||
{
|
||||
// forward 'error' events to the BinaryInterface
|
||||
return array('error');
|
||||
}
|
||||
}
|
||||
|
||||
$listener = new DebugListener();
|
||||
|
||||
$driver = CustomImplementation::load('php');
|
||||
|
||||
// adds listener
|
||||
$driver->listen($listener);
|
||||
|
||||
$driver->on('error', function ($line) {
|
||||
echo '[ERROR] ' . $line . PHP_EOL;
|
||||
});
|
||||
|
||||
// removes listener
|
||||
$driver->unlisten($listener);
|
||||
```
|
||||
|
||||
### Bundled listeners
|
||||
|
||||
The debug listener is a simple listener to catch `stderr` and `stdout` outputs ;
|
||||
read the implementation for customization.
|
||||
|
||||
```php
|
||||
use Alchemy\BinaryDriver\Listeners\DebugListener;
|
||||
|
||||
$driver = CustomImplementation::load('php');
|
||||
$driver->listen(new DebugListener());
|
||||
|
||||
$driver->on('debug', function ($line) {
|
||||
echo $line;
|
||||
});
|
||||
```
|
||||
|
||||
## ProcessBuilderFactory
|
||||
|
||||
ProcessBuilderFactory ease spawning processes by generating Symfony [Process]
|
||||
(http://symfony.com/doc/master/components/process.html) objects.
|
||||
|
||||
```php
|
||||
use Alchemy\BinaryDriver\ProcessBuilderFactory;
|
||||
|
||||
$factory = new ProcessBuilderFactory('/usr/bin/php');
|
||||
|
||||
// return a Symfony\Component\Process\Process
|
||||
$process = $factory->create('-v');
|
||||
|
||||
// echoes '/usr/bin/php' '-v'
|
||||
echo $process->getCommandLine();
|
||||
|
||||
$process = $factory->create(array('-r', 'echo "Hello !";'));
|
||||
|
||||
// echoes '/usr/bin/php' '-r' 'echo "Hello !";'
|
||||
echo $process->getCommandLine();
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
A simple configuration object, providing an `ArrayAccess` and `IteratorAggregate`
|
||||
interface.
|
||||
|
||||
```php
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
|
||||
$conf = new Configuration(array('timeout' => 0));
|
||||
|
||||
echo $conf->get('timeout');
|
||||
|
||||
if ($conf->has('param')) {
|
||||
$conf->remove('param');
|
||||
}
|
||||
|
||||
$conf->set('timeout', 20);
|
||||
|
||||
$conf->all();
|
||||
```
|
||||
|
||||
Same example using the `ArrayAccess` interface :
|
||||
|
||||
```php
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
|
||||
$conf = new Configuration(array('timeout' => 0));
|
||||
|
||||
echo $conf['timeout'];
|
||||
|
||||
if (isset($conf['param'])) {
|
||||
unset($conf['param']);
|
||||
}
|
||||
|
||||
$conf['timeout'] = 20;
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is released under the MIT license.
|
||||
734
README.md
734
README.md
|
|
@ -1,736 +1,60 @@
|
|||
# PHP-FFMPEG
|
||||
|
||||
[](https://packagist.org/packages/PHP-FFMpeg/PHP-FFMpeg)
|
||||
[](LICENSE.md)
|
||||

|
||||
[](https://packagist.org/packages/PHP-FFMpeg/PHP-FFMpeg)
|
||||
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/support-ukraine.svg?t=1" />](https://supportukrainenow.org)
|
||||
|
||||
An Object-Oriented library to convert video/audio files with FFmpeg / AVConv.
|
||||
# This is my package ffmpeg-mappable-media
|
||||
|
||||
## Your attention please
|
||||
[](https://packagist.org/packages/danjones000/ffmpeg-mappable-media)
|
||||
[](https://codeberg.org/danjones000/ffmpeg-mappable-media/actions/workflows/run-tests.yml)
|
||||
[](https://packagist.org/packages/danjones000/ffmpeg-mappable-media)
|
||||
|
||||
### How this library works:
|
||||
This is where your description should go. Try and limit it to a paragraph or two. Consider adding a small example.
|
||||
|
||||
This library requires a working [FFMpeg install](https://ffmpeg.org/download.html). You will need both FFMpeg and FFProbe binaries to use it.
|
||||
Be sure that these binaries can be located with system PATH to get the benefit of the binary detection,
|
||||
otherwise you should have to explicitly give the binaries path on load.
|
||||
## Support us
|
||||
|
||||
### Known issues:
|
||||
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/ffmpeg-mappable-media.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/ffmpeg-mappable-media)
|
||||
|
||||
- Using rotate and resize will produce a corrupted output when using
|
||||
[libav](http://libav.org/) 0.8. The bug is fixed in version 9. This bug does not
|
||||
appear in latest ffmpeg version.
|
||||
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
|
||||
|
||||
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
|
||||
|
||||
## Installation
|
||||
|
||||
This library requires PHP 8.0 or higher. For older versions of PHP, check out the [0.x-branch](https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/0.x).
|
||||
|
||||
The recommended way to install PHP-FFMpeg is through [Composer](https://getcomposer.org).
|
||||
You can install the package via composer:
|
||||
|
||||
```bash
|
||||
$ composer require php-ffmpeg/php-ffmpeg
|
||||
composer require danjones000/ffmpeg-mappable-media
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
## Usage
|
||||
|
||||
```php
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$ffmpeg = FFMpeg\FFMpeg::create();
|
||||
$video = $ffmpeg->open('video.mpg');
|
||||
$video
|
||||
->filters()
|
||||
->resize(new FFMpeg\Coordinate\Dimension(320, 240))
|
||||
->synchronize();
|
||||
$video
|
||||
->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(10))
|
||||
->save('frame.jpg');
|
||||
$video
|
||||
->save(new FFMpeg\Format\Video\X264(), 'export-x264.mp4')
|
||||
->save(new FFMpeg\Format\Video\WMV(), 'export-wmv.wmv')
|
||||
->save(new FFMpeg\Format\Video\WebM(), 'export-webm.webm');
|
||||
$skeleton = new Danjones\FFMpeg();
|
||||
echo $skeleton->echoPhrase('Hello, Danjones!');
|
||||
```
|
||||
|
||||
## Documentation
|
||||
## Testing
|
||||
|
||||
This documentation is an introduction to discover the API. It's recommended
|
||||
to browse the source code as it is self-documented.
|
||||
|
||||
### FFMpeg
|
||||
|
||||
`FFMpeg\FFMpeg` is the main object to use to manipulate medias. To build it,
|
||||
use the static `FFMpeg\FFMpeg::create`:
|
||||
|
||||
```php
|
||||
$ffmpeg = FFMpeg\FFMpeg::create();
|
||||
```bash
|
||||
composer test
|
||||
```
|
||||
|
||||
FFMpeg will autodetect ffmpeg and ffprobe binaries. If you want to give binary
|
||||
paths explicitly, you can pass an array as configuration. A `Psr\Logger\LoggerInterface`
|
||||
can also be passed to log binary executions.
|
||||
## Changelog
|
||||
|
||||
```php
|
||||
$ffmpeg = FFMpeg\FFMpeg::create(array(
|
||||
'ffmpeg.binaries' => '/opt/local/ffmpeg/bin/ffmpeg',
|
||||
'ffprobe.binaries' => '/opt/local/ffmpeg/bin/ffprobe',
|
||||
'timeout' => 3600, // The timeout for the underlying process
|
||||
'ffmpeg.threads' => 12, // The number of threads that FFMpeg should use
|
||||
), $logger);
|
||||
```
|
||||
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
|
||||
|
||||
You may pass a `temporary_directory` key to specify a path for temporary files.
|
||||
## Contributing
|
||||
|
||||
```php
|
||||
$ffmpeg = FFMpeg\FFMpeg::create(array(
|
||||
'temporary_directory' => '/var/ffmpeg-tmp'
|
||||
), $logger);
|
||||
```
|
||||
Please see [CONTRIBUTING](https://codeberg.org/spatie/.github/blob/main/CONTRIBUTING.md) for details.
|
||||
|
||||
### Manipulate media
|
||||
## Security Vulnerabilities
|
||||
|
||||
`FFMpeg\FFMpeg` creates media based on URIs. URIs could be either a pointer to a
|
||||
local filesystem resource, an HTTP resource or any resource supported by FFmpeg.
|
||||
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
|
||||
|
||||
**Note**: To list all supported resource type of your FFmpeg build, use the
|
||||
`-protocols` command:
|
||||
## Credits
|
||||
|
||||
```
|
||||
ffmpeg -protocols
|
||||
```
|
||||
|
||||
To open a resource, use the `FFMpeg\FFMpeg::open` method.
|
||||
|
||||
```php
|
||||
$ffmpeg->open('video.mpeg');
|
||||
```
|
||||
|
||||
Two types of media can be resolved: `FFMpeg\Media\Audio` and `FFMpeg\Media\Video`.
|
||||
A third type, `FFMpeg\Media\Frame`, is available through videos.
|
||||
|
||||
### Video
|
||||
|
||||
`FFMpeg\Media\Video` can be transcoded, ie: change codec, isolate audio or
|
||||
video. Frames can be extracted.
|
||||
|
||||
##### Transcoding
|
||||
|
||||
You can transcode videos using the `FFMpeg\Media\Video:save` method. You will
|
||||
pass a `FFMpeg\Format\FormatInterface` for that.
|
||||
|
||||
Please note that audio and video bitrate are set on the format. You can disable the `-b:v` option by setting the kilo bitrate to 0.
|
||||
|
||||
```php
|
||||
$format = new FFMpeg\Format\Video\X264();
|
||||
$format->on('progress', function ($video, $format, $percentage) {
|
||||
echo "$percentage % transcoded";
|
||||
});
|
||||
|
||||
$format
|
||||
->setKiloBitrate(1000)
|
||||
->setAudioChannels(2)
|
||||
->setAudioKiloBitrate(256);
|
||||
|
||||
$video->save($format, 'video.avi');
|
||||
```
|
||||
|
||||
Transcoding progress can be monitored in realtime, see Format documentation
|
||||
below for more information.
|
||||
|
||||
##### Extracting image
|
||||
|
||||
You can extract a frame at any timecode using the `FFMpeg\Media\Video::frame`
|
||||
method.
|
||||
|
||||
This code returns a `FFMpeg\Media\Frame` instance corresponding to the second 42.
|
||||
You can pass any `FFMpeg\Coordinate\TimeCode` as argument, see dedicated
|
||||
documentation below for more information.
|
||||
|
||||
```php
|
||||
$frame = $video->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(42));
|
||||
$frame->save('image.jpg');
|
||||
```
|
||||
|
||||
If you want to extract multiple images from the video, you can use the following filter:
|
||||
|
||||
```php
|
||||
$video
|
||||
->filters()
|
||||
->extractMultipleFrames(FFMpeg\Filters\Video\ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, '/path/to/destination/folder/')
|
||||
->synchronize();
|
||||
|
||||
$video
|
||||
->save(new FFMpeg\Format\Video\X264(), '/path/to/new/file');
|
||||
```
|
||||
By default, this will save the frames as `jpg` images.
|
||||
|
||||
You are able to override this using `setFrameFileType` to save the frames in another format:
|
||||
```php
|
||||
$frameFileType = 'jpg'; // either 'jpg', 'jpeg' or 'png'
|
||||
$filter = new ExtractMultipleFramesFilter($frameRate, $destinationFolder);
|
||||
$filter->setFrameFileType($frameFileType);
|
||||
|
||||
$video->addFilter($filter);
|
||||
```
|
||||
|
||||
##### Clip
|
||||
|
||||
Cuts the video at a desired point. Use input seeking method. It is faster option than use filter clip.
|
||||
|
||||
```php
|
||||
$clip = $video->clip(FFMpeg\Coordinate\TimeCode::fromSeconds(30), FFMpeg\Coordinate\TimeCode::fromSeconds(15));
|
||||
$clip->save(new FFMpeg\Format\Video\X264(), 'video.avi');
|
||||
```
|
||||
|
||||
The clip filter takes two parameters:
|
||||
|
||||
- `$start`, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the start point of the clip
|
||||
- `$duration`, optional, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the duration of the clip
|
||||
|
||||
On clip you can apply same filters as on video. For example resizing filter.
|
||||
|
||||
```php
|
||||
$clip = $video->clip(FFMpeg\Coordinate\TimeCode::fromSeconds(30), FFMpeg\Coordinate\TimeCode::fromSeconds(15));
|
||||
$clip->filters()->resize(new FFMpeg\Coordinate\Dimension(320, 240), FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_INSET, true);
|
||||
$clip->save(new FFMpeg\Format\Video\X264(), 'video.avi');
|
||||
```
|
||||
|
||||
##### Generate a waveform
|
||||
|
||||
You can generate a waveform of an audio file using the `FFMpeg\Media\Audio::waveform`
|
||||
method.
|
||||
|
||||
This code returns a `FFMpeg\Media\Waveform` instance.
|
||||
You can optionally pass dimensions as the first two arguments and an array of hex string colors for ffmpeg to use for the waveform, see dedicated
|
||||
documentation below for more information.
|
||||
|
||||
The output file MUST use the PNG extension.
|
||||
|
||||
```php
|
||||
$waveform = $audio->waveform(640, 120, array('#00FF00'));
|
||||
$waveform->save('waveform.png');
|
||||
```
|
||||
|
||||
If you want to get a waveform from a video, convert it in an audio file first.
|
||||
|
||||
```php
|
||||
// Open your video file
|
||||
$video = $ffmpeg->open( 'video.mp4' );
|
||||
|
||||
// Set an audio format
|
||||
$audio_format = new FFMpeg\Format\Audio\Mp3();
|
||||
|
||||
// Extract the audio into a new file as mp3
|
||||
$video->save($audio_format, 'audio.mp3');
|
||||
|
||||
// Set the audio file
|
||||
$audio = $ffmpeg->open( 'audio.mp3' );
|
||||
|
||||
// Create the waveform
|
||||
$waveform = $audio->waveform();
|
||||
$waveform->save( 'waveform.png' );
|
||||
```
|
||||
|
||||
##### Filters
|
||||
|
||||
You can apply filters on `FFMpeg\Media\Video` with the `FFMpeg\Media\Video::addFilter`
|
||||
method. Video accepts Audio and Video filters.
|
||||
|
||||
You can build your own filters and some are bundled in PHP-FFMpeg - they are
|
||||
accessible through the `FFMpeg\Media\Video::filters` method.
|
||||
|
||||
Filters are chainable
|
||||
|
||||
```php
|
||||
$video
|
||||
->filters()
|
||||
->resize($dimension, $mode, $useStandards)
|
||||
->framerate($framerate, $gop)
|
||||
->synchronize();
|
||||
```
|
||||
|
||||
###### Rotate
|
||||
|
||||
Rotates a video to a given angle.
|
||||
|
||||
```php
|
||||
$video->filters()->rotate($angle);
|
||||
```
|
||||
|
||||
The `$angle` parameter must be one of the following constants :
|
||||
|
||||
- `FFMpeg\Filters\Video\RotateFilter::ROTATE_90`: 90° clockwise
|
||||
- `FFMpeg\Filters\Video\RotateFilter::ROTATE_180`: 180°
|
||||
- `FFMpeg\Filters\Video\RotateFilter::ROTATE_270`: 90° counterclockwise
|
||||
|
||||
###### Resize
|
||||
|
||||
Resizes a video to a given size.
|
||||
|
||||
```php
|
||||
$video->filters()->resize($dimension, $mode, $useStandards);
|
||||
```
|
||||
|
||||
The resize filter takes three parameters:
|
||||
|
||||
- `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`
|
||||
- `$mode`, one of the constants `FFMpeg\Filters\Video\ResizeFilter::RESIZEMODE_*` constants
|
||||
- `$useStandards`, a boolean to force the use of the nearest aspect ratio standard.
|
||||
|
||||
If you want a video in a non-standard ratio, you can use the padding filter to resize your video in the desired size, and wrap it into black bars.
|
||||
|
||||
```php
|
||||
$video->filters()->pad($dimension);
|
||||
```
|
||||
|
||||
The pad filter takes one parameter:
|
||||
|
||||
- `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`
|
||||
|
||||
Don't forget to save it afterwards.
|
||||
|
||||
```php
|
||||
$video->save(new FFMpeg\Format\Video\X264(), $new_file);
|
||||
```
|
||||
|
||||
###### Watermark
|
||||
|
||||
Watermark a video with a given image.
|
||||
|
||||
```php
|
||||
$video
|
||||
->filters()
|
||||
->watermark($watermarkPath, array(
|
||||
'position' => 'relative',
|
||||
'bottom' => 50,
|
||||
'right' => 50,
|
||||
));
|
||||
```
|
||||
|
||||
The watermark filter takes two parameters:
|
||||
|
||||
`$watermarkPath`, the path to your watermark file.
|
||||
`$coordinates`, an array defining how you want your watermark positioned. You can use relative positioning as demonstrated above or absolute as such:
|
||||
|
||||
```php
|
||||
$video
|
||||
->filters()
|
||||
->watermark($watermarkPath, array(
|
||||
'position' => 'absolute',
|
||||
'x' => 1180,
|
||||
'y' => 620,
|
||||
));
|
||||
```
|
||||
|
||||
###### Framerate
|
||||
|
||||
Changes the frame rate of the video.
|
||||
|
||||
```php
|
||||
$video->filters()->framerate($framerate, $gop);
|
||||
```
|
||||
|
||||
The framerate filter takes two parameters:
|
||||
|
||||
- `$framerate`, an instance of `FFMpeg\Coordinate\FrameRate`
|
||||
- `$gop`, a [GOP](https://wikipedia.org/wiki/Group_of_pictures) value (integer)
|
||||
|
||||
###### Synchronize
|
||||
|
||||
Synchronizes audio and video.
|
||||
|
||||
Some containers may use a delay that results in desynchronized outputs. This
|
||||
filter solves this issue.
|
||||
|
||||
```php
|
||||
$video->filters()->synchronize();
|
||||
```
|
||||
|
||||
###### Clip
|
||||
|
||||
Cuts the video at a desired point.
|
||||
|
||||
```php
|
||||
$video->filters()->clip(FFMpeg\Coordinate\TimeCode::fromSeconds(30), FFMpeg\Coordinate\TimeCode::fromSeconds(15));
|
||||
```
|
||||
|
||||
The clip filter takes two parameters:
|
||||
|
||||
- `$start`, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the start point of the clip
|
||||
- `$duration`, optional, an instance of `FFMpeg\Coordinate\TimeCode`, specifies the duration of the clip
|
||||
|
||||
###### Crop
|
||||
|
||||
Crops the video based on a width and height(a `Point`)
|
||||
|
||||
```php
|
||||
$video->filters()->crop(new FFMpeg\Coordinate\Point("t*100", 0, true), new FFMpeg\Coordinate\Dimension(200, 600));
|
||||
```
|
||||
|
||||
It takes two parameters:
|
||||
- `$point`, an instance of `FFMpeg\Coordinate\Point`, specifies the point to crop
|
||||
- `$dimension`, an instance of `FFMpeg\Coordinate\Dimension`, specifies the dimension of the output video
|
||||
|
||||
### Audio
|
||||
|
||||
`FFMpeg\Media\Audio` can be transcoded too, ie: change codec, isolate audio or
|
||||
video. Frames can be extracted.
|
||||
|
||||
##### Transcoding
|
||||
|
||||
You can transcode audios using the `FFMpeg\Media\Audio:save` method. You will
|
||||
pass a `FFMpeg\Format\FormatInterface` for that.
|
||||
|
||||
Please note that audio kilobitrate is set on the audio format.
|
||||
|
||||
```php
|
||||
$ffmpeg = FFMpeg\FFMpeg::create();
|
||||
$audio = $ffmpeg->open('track.mp3');
|
||||
|
||||
$format = new FFMpeg\Format\Audio\Flac();
|
||||
$format->on('progress', function ($audio, $format, $percentage) {
|
||||
echo "$percentage % transcoded";
|
||||
});
|
||||
|
||||
$format
|
||||
->setAudioChannels(2)
|
||||
->setAudioKiloBitrate(256);
|
||||
|
||||
$audio->save($format, 'track.flac');
|
||||
```
|
||||
|
||||
Transcoding progress can be monitored in realtime, see Format documentation
|
||||
below for more information.
|
||||
|
||||
##### Filters
|
||||
|
||||
You can apply filters on `FFMpeg\Media\Audio` with the `FFMpeg\Media\Audio::addFilter`
|
||||
method. It only accepts audio filters.
|
||||
|
||||
You can build your own filters and some are bundled in PHP-FFMpeg - they are
|
||||
accessible through the `FFMpeg\Media\Audio::filters` method.
|
||||
|
||||
##### Clipping
|
||||
Cuts the audio at a desired point.
|
||||
|
||||
```php
|
||||
$audio->filters()->clip(FFMpeg\Coordinate\TimeCode::fromSeconds(30), FFMpeg\Coordinate\TimeCode::fromSeconds(15));
|
||||
```
|
||||
|
||||
|
||||
###### Metadata
|
||||
|
||||
Add metadata to audio files. Just pass an array of key=value pairs of all
|
||||
metadata you would like to add. If no arguments are passed into the filter
|
||||
all metadata will be removed from input file. Currently supported data is
|
||||
title, artist, album, artist, composer, track, year, description, artwork
|
||||
|
||||
```php
|
||||
$audio->filters()->addMetadata(["title" => "Some Title", "track" => 1]);
|
||||
|
||||
//remove all metadata and video streams from audio file
|
||||
$audio->filters()->addMetadata();
|
||||
```
|
||||
|
||||
Add artwork to the audio file
|
||||
```php
|
||||
$audio->filters()->addMetadata(["artwork" => "/path/to/image/file.jpg"]);
|
||||
```
|
||||
NOTE: at present ffmpeg (version 3.2.2) only supports artwork output for .mp3
|
||||
files
|
||||
|
||||
###### Resample
|
||||
|
||||
Resamples an audio file.
|
||||
|
||||
```php
|
||||
$audio->filters()->resample($rate);
|
||||
```
|
||||
|
||||
The resample filter takes two parameters :
|
||||
|
||||
- `$rate`, a valid audio sample rate value (integer)
|
||||
|
||||
#### Frame
|
||||
|
||||
A frame is an image at a timecode of a video; see documentation above about
|
||||
frame extraction.
|
||||
|
||||
You can save frames using the `FFMpeg\Media\Frame::save` method.
|
||||
|
||||
```php
|
||||
$frame->save('target.jpg');
|
||||
```
|
||||
|
||||
This method has a second optional boolean parameter. Set it to true to get
|
||||
accurate images; it takes more time to execute.
|
||||
|
||||
#### Gif
|
||||
|
||||
A gif is an animated image extracted from a sequence of the video.
|
||||
|
||||
You can save gif files using the `FFMpeg\Media\Gif::save` method.
|
||||
|
||||
```php
|
||||
$video = $ffmpeg->open( '/path/to/video' );
|
||||
$video
|
||||
->gif(FFMpeg\Coordinate\TimeCode::fromSeconds(2), new FFMpeg\Coordinate\Dimension(640, 480), 3)
|
||||
->save($new_file);
|
||||
```
|
||||
|
||||
This method has a third optional boolean parameter, which is the duration of the animation.
|
||||
If you don't set it, you will get a fixed gif image.
|
||||
|
||||
#### Concatenation
|
||||
|
||||
This feature allows you to generate one audio or video file, based on multiple sources.
|
||||
|
||||
There are two ways to concatenate videos, depending on the codecs of the sources.
|
||||
If your sources have all been encoded with the same codec, you will want to use the `FFMpeg\Media\Concatenate::saveFromSameCodecs` which has way better performances.
|
||||
If your sources have been encoded with different codecs, you will want to use the `FFMpeg\Media\Concatenate::saveFromDifferentCodecs`.
|
||||
|
||||
The first function will use the initial codec as the one for the generated file.
|
||||
With the second function, you will be able to choose which codec you want for the generated file.
|
||||
|
||||
You also need to pay attention to the fact that, when using the saveFromDifferentCodecs method,
|
||||
your files MUST have video and audio streams.
|
||||
|
||||
In both cases, you will have to provide an array of files.
|
||||
|
||||
To concatenate videos encoded with the same codec, do as follow:
|
||||
|
||||
```php
|
||||
// In order to instantiate the video object, you HAVE TO pass a path to a valid video file.
|
||||
// We recommend that you put there the path of any of the video you want to use in this concatenation.
|
||||
$video = $ffmpeg->open( '/path/to/video' );
|
||||
$video
|
||||
->concat(array('/path/to/video1', '/path/to/video2'))
|
||||
->saveFromSameCodecs('/path/to/new_file', TRUE);
|
||||
```
|
||||
|
||||
The boolean parameter of the save function allows you to use the copy parameter which accelerates drastically the generation of the encoded file.
|
||||
|
||||
To concatenate videos encoded with the different codec, do as follow:
|
||||
|
||||
```php
|
||||
// In order to instantiate the video object, you HAVE TO pass a path to a valid video file.
|
||||
// We recommend that you put there the path of any of the video you want to use in this concatenation.
|
||||
$video = $ffmpeg->open( '/path/to/video' );
|
||||
|
||||
$format = new FFMpeg\Format\Video\X264();
|
||||
$format->setAudioCodec("libmp3lame");
|
||||
|
||||
$video
|
||||
->concat(array('/path/to/video1', '/path/to/video2'))
|
||||
->saveFromDifferentCodecs($format, '/path/to/new_file');
|
||||
```
|
||||
|
||||
More details about concatenation in FFMPEG can be found [here](https://trac.ffmpeg.org/wiki/Concatenate), [here](https://ffmpeg.org/ffmpeg-formats.html#concat-1) and [here](https://ffmpeg.org/ffmpeg.html#Stream-copy).
|
||||
|
||||
### AdvancedMedia
|
||||
AdvancedMedia may have multiple inputs and multiple outputs.
|
||||
|
||||
This class has been developed primarily to use with `-filter_complex`.
|
||||
|
||||
So, its `filters()` method accepts only filters that can be used inside `-filter_complex` command.
|
||||
AdvancedMedia already contains some built-in filters.
|
||||
|
||||
#### Base usage
|
||||
For example:
|
||||
|
||||
```php
|
||||
$advancedMedia = $ffmpeg->openAdvanced(array('video_1.mp4', 'video_2.mp4'));
|
||||
$advancedMedia->filters()
|
||||
->custom('[0:v][1:v]', 'hstack', '[v]');
|
||||
$advancedMedia
|
||||
->map(array('0:a', '[v]'), new X264('aac', 'libx264'), 'output.mp4')
|
||||
->save();
|
||||
```
|
||||
|
||||
This code takes 2 input videos, stacks they horizontally in 1 output video and adds to this new video the audio from the first video.
|
||||
(It is impossible with simple filtergraph that has only 1 input and only 1 output).
|
||||
|
||||
|
||||
#### Complicated example
|
||||
A more difficult example of possibilities of the AdvancedMedia. Consider all input videos already have the same resolution and duration. ("xstack" filter has been added in the 4.1 version of the ffmpeg).
|
||||
|
||||
```php
|
||||
$inputs = array(
|
||||
'video_1.mp4',
|
||||
'video_2.mp4',
|
||||
'video_3.mp4',
|
||||
'video_4.mp4',
|
||||
);
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->custom('[0:v]', 'negate', '[v0negate]')
|
||||
->custom('[1:v]', 'edgedetect', '[v1edgedetect]')
|
||||
->custom('[2:v]', 'hflip', '[v2hflip]')
|
||||
->custom('[3:v]', 'vflip', '[v3vflip]')
|
||||
->xStack('[v0negate][v1edgedetect][v2hflip][v3vflip]', XStackFilter::LAYOUT_2X2, 4, '[resultv]');
|
||||
$advancedMedia
|
||||
->map(array('0:a'), new Mp3(), 'video_1.mp3')
|
||||
->map(array('1:a'), new Flac(), 'video_2.flac')
|
||||
->map(array('2:a'), new Wav(), 'video_3.wav')
|
||||
->map(array('3:a'), new Aac(), 'video_4.aac')
|
||||
->map(array('[resultv]'), new X264('aac', 'libx264'), 'output.mp4')
|
||||
->save();
|
||||
```
|
||||
|
||||
This code takes 4 input videos, then the negates the first video, stores result in `[v0negate]` stream, detects edges in the second video, stores result in `[v1edgedetect]` stream, horizontally flips the third video, stores result in `[v2hflip]` stream, vertically flips the fourth video, stores result in `[v3vflip]` stream, then takes this 4 generated streams ans combine them in one 2x2 collage video.
|
||||
Then saves audios from the original videos into the 4 different formats and saves the generated collage video into the separate file.
|
||||
|
||||
As you can see, you can take multiple input sources, perform the complicated processing for them and produce multiple output files in the same time, in the one ffmpeg command.
|
||||
|
||||
#### Just give me a map!
|
||||
You do not have to use `-filter_complex`. You can use only `-map` options. For example, just extract the audio from the video:
|
||||
|
||||
```php
|
||||
$advancedMedia = $ffmpeg->openAdvanced(array('video.mp4'));
|
||||
$advancedMedia
|
||||
->map(array('0:a'), new Mp3(), 'output.mp3')
|
||||
->save();
|
||||
```
|
||||
|
||||
#### Customisation
|
||||
If you need you can extra customize the result ffmpeg command of the AdvancedMedia:
|
||||
|
||||
```php
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia
|
||||
->setInitialParameters(array('the', 'params', 'that', 'will', 'be', 'added', 'before', '-i', 'part', 'of', 'the', 'command'))
|
||||
->setAdditionalParameters(array('the', 'params', 'that', 'will', 'be', 'added', 'at', 'the', 'end', 'of', 'the', 'command'));
|
||||
```
|
||||
|
||||
#### Formats
|
||||
|
||||
A format implements `FFMpeg\Format\FormatInterface`. To save to a video file,
|
||||
use `FFMpeg\Format\VideoInterface`, and `FFMpeg\Format\AudioInterface` for
|
||||
audio files.
|
||||
|
||||
A format can also extend `FFMpeg\Format\ProgressableInterface` to get realtime
|
||||
information about the transcoding.
|
||||
|
||||
Predefined formats already provide progress information as events.
|
||||
|
||||
```php
|
||||
$format = new FFMpeg\Format\Video\X264();
|
||||
$format->on('progress', function ($video, $format, $percentage) {
|
||||
echo "$percentage % transcoded";
|
||||
});
|
||||
|
||||
$video->save($format, 'video.avi');
|
||||
```
|
||||
|
||||
The callback provided for the event can be any callable.
|
||||
|
||||
##### Add additional parameters
|
||||
|
||||
You can add additional parameters to your encoding requests based on your video format.
|
||||
|
||||
The argument of the setAdditionalParameters method is an array.
|
||||
|
||||
```php
|
||||
$format = new FFMpeg\Format\Video\X264();
|
||||
$format->setAdditionalParameters(array('foo', 'bar'));
|
||||
$video->save($format, 'video.avi');
|
||||
```
|
||||
|
||||
##### Add initial parameters
|
||||
|
||||
You can also add initial parameters to your encoding requests based on your video format. This can be expecially handy in overriding a default input codec in FFMpeg.
|
||||
|
||||
The argument of the setInitialParameters method is an array.
|
||||
|
||||
```php
|
||||
$format = new FFMpeg\Format\Video\X264();
|
||||
$format->setInitialParameters(array('-acodec', 'libopus'));
|
||||
$video->save($format, 'video.avi');
|
||||
```
|
||||
|
||||
##### Create your own format
|
||||
|
||||
The easiest way to create a format is to extend the abstract
|
||||
`FFMpeg\Format\Video\DefaultVideo` and `FFMpeg\Format\Audio\DefaultAudio`.
|
||||
and implement the following methods.
|
||||
|
||||
```php
|
||||
class CustomWMVFormat extends FFMpeg\Format\Video\DefaultVideo
|
||||
{
|
||||
public function __construct($audioCodec = 'wmav2', $videoCodec = 'wmv2')
|
||||
{
|
||||
$this
|
||||
->setAudioCodec($audioCodec)
|
||||
->setVideoCodec($videoCodec);
|
||||
}
|
||||
|
||||
public function supportBFrames()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getAvailableAudioCodecs()
|
||||
{
|
||||
return array('wmav2');
|
||||
}
|
||||
|
||||
public function getAvailableVideoCodecs()
|
||||
{
|
||||
return array('wmv2');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Coordinates
|
||||
|
||||
FFMpeg uses many units for time and space coordinates.
|
||||
|
||||
- `FFMpeg\Coordinate\AspectRatio` represents an aspect ratio.
|
||||
- `FFMpeg\Coordinate\Dimension` represent a dimension.
|
||||
- `FFMpeg\Coordinate\FrameRate` represent a framerate.
|
||||
- `FFMpeg\Coordinate\Point` represent a point. (Supports dynamic points since v0.10.0)
|
||||
- `FFMpeg\Coordinate\TimeCode` represent a timecode.
|
||||
|
||||
### FFProbe
|
||||
|
||||
`FFMpeg\FFProbe` is used internally by `FFMpeg\FFMpeg` to probe medias. You can
|
||||
also use it to extract media metadata.
|
||||
|
||||
```php
|
||||
$ffprobe = FFMpeg\FFProbe::create();
|
||||
$ffprobe
|
||||
->streams('/path/to/video/mp4') // extracts streams informations
|
||||
->videos() // filters video streams
|
||||
->first() // returns the first video stream
|
||||
->get('codec_name'); // returns the codec_name property
|
||||
```
|
||||
|
||||
```php
|
||||
$ffprobe = FFMpeg\FFProbe::create();
|
||||
$ffprobe
|
||||
->format('/path/to/video/mp4') // extracts file informations
|
||||
->get('duration'); // returns the duration property
|
||||
```
|
||||
|
||||
### Validating media files
|
||||
|
||||
(since 0.10.0)
|
||||
You can validate media files using PHP-FFMpeg's FFProbe wrapper.
|
||||
|
||||
```php
|
||||
$ffprobe = FFMpeg\FFProbe::create();
|
||||
$ffprobe->isValid('/path/to/file/to/check'); // returns bool
|
||||
```
|
||||
- [Dan Jones](https://codeberg.org/danjones000)
|
||||
- [All Contributors](../../contributors)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](http://opensource.org/licenses/MIT).
|
||||
|
||||
Music: "Favorite Secrets" by Waylon Thornton
|
||||
From the Free Music Archive
|
||||
[CC BY NC SA](http://creativecommons.org/licenses/by-nc-sa/3.0/us/)
|
||||
|
||||
Music: "Siesta" by Jahzzar
|
||||
From the Free Music Archive
|
||||
[CC BY SA](https://creativecommons.org/licenses/by-sa/3.0/)
|
||||
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "php-ffmpeg/php-ffmpeg",
|
||||
"type": "library",
|
||||
"description": "FFMpeg PHP, an Object Oriented library to communicate with AVconv / ffmpeg",
|
||||
"name": "danjones000/ffmpeg-mappable-media",
|
||||
"description": "This is my package ffmpeg-mappable-media",
|
||||
"keywords": [
|
||||
"danjones000",
|
||||
"video processing",
|
||||
"video",
|
||||
"audio processing",
|
||||
|
|
@ -12,64 +12,46 @@
|
|||
"avprobe",
|
||||
"ffprobe"
|
||||
],
|
||||
"homepage": "https://codeberg.org/danjones000/ffmpeg-mappable-media",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Romain Neutron",
|
||||
"email": "imprec@gmail.com",
|
||||
"homepage": "http://www.lickmychip.com/"
|
||||
},
|
||||
{
|
||||
"name": "Phraseanet Team",
|
||||
"email": "info@alchemy.fr",
|
||||
"homepage": "http://www.phraseanet.com/"
|
||||
},
|
||||
{
|
||||
"name": "Patrik Karisch",
|
||||
"email": "patrik@karisch.guru",
|
||||
"homepage": "http://www.karisch.guru"
|
||||
},
|
||||
{
|
||||
"name": "Romain Biard",
|
||||
"email": "romain.biard@gmail.com",
|
||||
"homepage": "https://www.strime.io/"
|
||||
},
|
||||
{
|
||||
"name": "Jens Hausdorf",
|
||||
"email": "hello@jens-hausdorf.de",
|
||||
"homepage": "https://jens-hausdorf.de"
|
||||
},
|
||||
{
|
||||
"name": "Pascal Baljet",
|
||||
"email": "pascal@protone.media",
|
||||
"homepage": "https://protone.media"
|
||||
"name": "Dan Jones",
|
||||
"email": "danjones@goodevilgenius.org",
|
||||
"role": "Developer",
|
||||
"homepage": "https://goodevilgenius.org"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0 || ^8.1",
|
||||
"evenement/evenement": "^3.0",
|
||||
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||
"spatie/temporary-directory": "^2.0",
|
||||
"symfony/process": "^5.4 || ^6.0",
|
||||
"symfony/cache": "^5.4 || ^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"php-ffmpeg/extras": "A compilation of common audio & video drivers for PHP-FFMpeg"
|
||||
"php": "^8.0",
|
||||
"php-ffmpeg/php-ffmpeg": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
"mockery/mockery": "^1.5"
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"pestphp/pest": "^1.20",
|
||||
"spatie/ray": "^1.28"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FFMpeg\\": "src/FFMpeg",
|
||||
"Alchemy\\BinaryDriver\\": "src/Alchemy/BinaryDriver"
|
||||
"Danjones\\FFMpeg\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Alchemy\\Tests\\BinaryDriver\\": "tests/Alchemy/BinaryDriver",
|
||||
"Tests\\FFMpeg\\": "tests/FFMpeg"
|
||||
"Danjones\\FFMpeg\\Tests\\": "tests"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/pest",
|
||||
"test-coverage": "vendor/bin/pest --coverage",
|
||||
"format": "vendor/bin/php-cs-fixer fix --allow-risky=yes"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,39 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" verbose="false" bootstrap="tests/bootstrap.php">
|
||||
<php>
|
||||
<env name="SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT" value="true"/>
|
||||
<env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled" />
|
||||
</php>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
executionOrder="random"
|
||||
failOnWarning="true"
|
||||
failOnRisky="true"
|
||||
failOnEmptyTestSuite="true"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
verbose="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Alchemy">
|
||||
<directory>tests/Alchemy/BinaryDriver</directory>
|
||||
</testsuite>
|
||||
<testsuite name="FFMpeg Unit">
|
||||
<directory>tests/FFMpeg/Unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="FFMpeg Functional">
|
||||
<directory>tests/FFMpeg/Functional</directory>
|
||||
<testsuite name="Danjones Test Suite">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
<report>
|
||||
<html outputDirectory="build/coverage"/>
|
||||
<text outputFile="build/coverage.txt"/>
|
||||
<clover outputFile="build/logs/clover.xml"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<logging>
|
||||
<junit outputFile="build/report.junit.xml"/>
|
||||
</logging>
|
||||
</phpunit>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,300 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\AbstractBinary;
|
||||
use Alchemy\BinaryDriver\BinaryDriverTestCase;
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
use Alchemy\BinaryDriver\Exception\ExecutableNotFoundException;
|
||||
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
|
||||
class AbstractBinaryTest extends BinaryDriverTestCase
|
||||
{
|
||||
protected function getPhpBinary()
|
||||
{
|
||||
$finder = new ExecutableFinder();
|
||||
$php = $finder->find('php');
|
||||
|
||||
if (null === $php) {
|
||||
$this->markTestSkipped('Unable to find a php binary');
|
||||
}
|
||||
|
||||
return $php;
|
||||
}
|
||||
|
||||
public function testSimpleLoadWithBinaryPath()
|
||||
{
|
||||
$php = $this->getPhpBinary();
|
||||
$imp = Implementation::load($php);
|
||||
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
|
||||
|
||||
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
|
||||
}
|
||||
|
||||
public function testMultipleLoadWithBinaryPath()
|
||||
{
|
||||
$php = $this->getPhpBinary();
|
||||
$imp = Implementation::load(['/zz/path/to/unexisting/command', $php]);
|
||||
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
|
||||
|
||||
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
|
||||
}
|
||||
|
||||
public function testSimpleLoadWithBinaryName()
|
||||
{
|
||||
$php = $this->getPhpBinary();
|
||||
$imp = Implementation::load('php');
|
||||
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
|
||||
|
||||
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
|
||||
}
|
||||
|
||||
public function testMultipleLoadWithBinaryName()
|
||||
{
|
||||
$php = $this->getPhpBinary();
|
||||
$imp = Implementation::load(['bachibouzouk', 'php']);
|
||||
$this->assertInstanceOf('Alchemy\Tests\BinaryDriver\Implementation', $imp);
|
||||
|
||||
$this->assertEquals($php, $imp->getProcessBuilderFactory()->getBinary());
|
||||
}
|
||||
|
||||
public function testLoadWithMultiplePathExpectingAFailure()
|
||||
{
|
||||
$this->expectException(ExecutableNotFoundException::class);
|
||||
|
||||
Implementation::load(['bachibouzouk', 'moribon']);
|
||||
}
|
||||
|
||||
public function testLoadWithUniquePathExpectingAFailure()
|
||||
{
|
||||
$this->expectException(ExecutableNotFoundException::class);
|
||||
|
||||
Implementation::load('bachibouzouk');
|
||||
}
|
||||
|
||||
public function testLoadWithCustomLogger()
|
||||
{
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
$imp = Implementation::load('php', $logger);
|
||||
|
||||
$this->assertEquals($logger, $imp->getProcessRunner()->getLogger());
|
||||
}
|
||||
|
||||
public function testLoadWithCustomConfigurationAsArray()
|
||||
{
|
||||
$conf = ['timeout' => 200];
|
||||
$imp = Implementation::load('php', null, $conf);
|
||||
|
||||
$this->assertEquals($conf, $imp->getConfiguration()->all());
|
||||
}
|
||||
|
||||
public function testLoadWithCustomConfigurationAsObject()
|
||||
{
|
||||
$conf = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
$imp = Implementation::load('php', null, $conf);
|
||||
|
||||
$this->assertEquals($conf, $imp->getConfiguration());
|
||||
}
|
||||
|
||||
public function testProcessBuilderFactoryGetterAndSetters()
|
||||
{
|
||||
$imp = Implementation::load('php');
|
||||
$factory = $this->getMockBuilder('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface')->getMock();
|
||||
|
||||
$imp->setProcessBuilderFactory($factory);
|
||||
$this->assertEquals($factory, $imp->getProcessBuilderFactory());
|
||||
}
|
||||
|
||||
public function testConfigurationGetterAndSetters()
|
||||
{
|
||||
$imp = Implementation::load('php');
|
||||
$conf = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
|
||||
$imp->setConfiguration($conf);
|
||||
$this->assertEquals($conf, $imp->getConfiguration());
|
||||
}
|
||||
|
||||
public function testTimeoutIsSetOnConstruction()
|
||||
{
|
||||
$imp = Implementation::load('php', null, ['timeout' => 42]);
|
||||
$this->assertEquals(42, $imp->getProcessBuilderFactory()->getTimeout());
|
||||
}
|
||||
|
||||
public function testTimeoutIsSetOnConfigurationSetting()
|
||||
{
|
||||
$imp = Implementation::load('php', null);
|
||||
$imp->setConfiguration(new Configuration(['timeout' => 42]));
|
||||
$this->assertEquals(42, $imp->getProcessBuilderFactory()->getTimeout());
|
||||
}
|
||||
|
||||
public function testTimeoutIsSetOnProcessBuilderSetting()
|
||||
{
|
||||
$imp = Implementation::load('php', null, ['timeout' => 42]);
|
||||
|
||||
$factory = $this->getMockBuilder('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface')->getMock();
|
||||
$factory->expects($this->once())
|
||||
->method('setTimeout')
|
||||
->with(42);
|
||||
|
||||
$imp->setProcessBuilderFactory($factory);
|
||||
}
|
||||
|
||||
public function testListenRegistersAListener()
|
||||
{
|
||||
$imp = Implementation::load('php');
|
||||
|
||||
$listeners = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\Listeners')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$listener = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\ListenerInterface')->getMock();
|
||||
|
||||
$listeners->expects($this->once())
|
||||
->method('register')
|
||||
->with($this->equalTo($listener), $this->equalTo($imp));
|
||||
|
||||
$reflexion = new \ReflectionClass('Alchemy\BinaryDriver\AbstractBinary');
|
||||
$prop = $reflexion->getProperty('listenersManager');
|
||||
$prop->setAccessible(true);
|
||||
$prop->setValue($imp, $listeners);
|
||||
|
||||
$imp->listen($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCommandParameters
|
||||
*/
|
||||
public function testCommandRunsAProcess($parameters, $bypassErrors, $expectedParameters, $output)
|
||||
{
|
||||
$imp = Implementation::load('php');
|
||||
$factory = $this->getMockBuilder('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface')->getMock();
|
||||
$processRunner = $this->getMockBuilder('Alchemy\BinaryDriver\ProcessRunnerInterface')->getMock();
|
||||
|
||||
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$processRunner->expects($this->once())
|
||||
->method('run')
|
||||
->with($this->equalTo($process), $this->isInstanceOf('SplObjectStorage'), $this->equalTo($bypassErrors))
|
||||
->will($this->returnValue($output));
|
||||
|
||||
$factory->expects($this->once())
|
||||
->method('create')
|
||||
->with($expectedParameters)
|
||||
->will($this->returnValue($process));
|
||||
|
||||
$imp->setProcessBuilderFactory($factory);
|
||||
$imp->setProcessRunner($processRunner);
|
||||
|
||||
$this->assertEquals($output, $imp->command($parameters, $bypassErrors));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCommandWithListenersParameters
|
||||
*/
|
||||
public function testCommandWithTemporaryListeners($parameters, $bypassErrors, $expectedParameters, $output, $count, $listeners)
|
||||
{
|
||||
$imp = Implementation::load('php');
|
||||
$factory = $this->getMockBuilder('Alchemy\BinaryDriver\ProcessBuilderFactoryInterface')->getMock();
|
||||
$processRunner = $this->getMockBuilder('Alchemy\BinaryDriver\ProcessRunnerInterface')->getMock();
|
||||
|
||||
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$firstStorage = $secondStorage = null;
|
||||
|
||||
$processRunner->expects($this->exactly(2))
|
||||
->method('run')
|
||||
->with($this->equalTo($process), $this->isInstanceOf('SplObjectStorage'), $this->equalTo($bypassErrors))
|
||||
->will($this->returnCallback(function ($process, $storage, $errors) use ($output, &$firstStorage, &$secondStorage) {
|
||||
if (null === $firstStorage) {
|
||||
$firstStorage = $storage;
|
||||
} else {
|
||||
$secondStorage = $storage;
|
||||
}
|
||||
|
||||
return $output;
|
||||
}));
|
||||
|
||||
$factory->expects($this->exactly(2))
|
||||
->method('create')
|
||||
->with($expectedParameters)
|
||||
->will($this->returnValue($process));
|
||||
|
||||
$imp->setProcessBuilderFactory($factory);
|
||||
$imp->setProcessRunner($processRunner);
|
||||
|
||||
$this->assertEquals($output, $imp->command($parameters, $bypassErrors, $listeners));
|
||||
$this->assertCount($count, $firstStorage);
|
||||
$this->assertEquals($output, $imp->command($parameters, $bypassErrors));
|
||||
$this->assertCount(0, $secondStorage);
|
||||
}
|
||||
|
||||
public function provideCommandWithListenersParameters()
|
||||
{
|
||||
return [
|
||||
['-a', false, ['-a'], 'loubda', 2, [$this->getMockListener(), $this->getMockListener()]],
|
||||
['-a', false, ['-a'], 'loubda', 1, [$this->getMockListener()]],
|
||||
['-a', false, ['-a'], 'loubda', 1, $this->getMockListener()],
|
||||
['-a', false, ['-a'], 'loubda', 0, []],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideCommandParameters()
|
||||
{
|
||||
return [
|
||||
['-a', false, ['-a'], 'loubda'],
|
||||
['-a', true, ['-a'], 'loubda'],
|
||||
['-a -b', false, ['-a -b'], 'loubda'],
|
||||
[['-a'], false, ['-a'], 'loubda'],
|
||||
[['-a'], true, ['-a'], 'loubda'],
|
||||
[['-a', '-b'], false, ['-a', '-b'], 'loubda'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testUnlistenUnregistersAListener()
|
||||
{
|
||||
$imp = Implementation::load('php');
|
||||
|
||||
$listeners = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\Listeners')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$listener = $this->getMockBuilder('Alchemy\BinaryDriver\Listeners\ListenerInterface')->getMock();
|
||||
|
||||
$listeners->expects($this->once())
|
||||
->method('unregister')
|
||||
->with($this->equalTo($listener), $this->equalTo($imp));
|
||||
|
||||
$reflexion = new \ReflectionClass('Alchemy\BinaryDriver\AbstractBinary');
|
||||
$prop = $reflexion->getProperty('listenersManager');
|
||||
$prop->setAccessible(true);
|
||||
$prop->setValue($imp, $listeners);
|
||||
|
||||
$imp->unlisten($listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PHPUnit_Framework_MockObject_MockObject
|
||||
*/
|
||||
private function getMockListener()
|
||||
{
|
||||
$listener = $this->getMockBuilder(ListenerInterface::class)->getMock();
|
||||
$listener->expects($this->any())
|
||||
->method('forwardedEvents')
|
||||
->willReturn([]);
|
||||
|
||||
return $listener;
|
||||
}
|
||||
}
|
||||
|
||||
class Implementation extends AbstractBinary
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'Implementation';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\Exception\InvalidArgumentException;
|
||||
use Alchemy\BinaryDriver\ProcessBuilderFactory;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
|
||||
abstract class AbstractProcessBuilderFactoryTest extends TestCase
|
||||
{
|
||||
public static $phpBinary;
|
||||
|
||||
private $original;
|
||||
/**
|
||||
* @return ProcessBuilderFactory
|
||||
*/
|
||||
abstract protected function getProcessBuilderFactory($binary);
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
ProcessBuilderFactory::$emulateSfLTS = null;
|
||||
if (null === static::$phpBinary) {
|
||||
$this->markTestSkipped('Unable to detect php binary, skipping');
|
||||
}
|
||||
}
|
||||
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
$finder = new ExecutableFinder();
|
||||
static::$phpBinary = $finder->find('php');
|
||||
}
|
||||
|
||||
public function testThatBinaryIsSetOnConstruction()
|
||||
{
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$this->assertEquals(static::$phpBinary, $factory->getBinary());
|
||||
}
|
||||
|
||||
public function testGetSetBinary()
|
||||
{
|
||||
$finder = new ExecutableFinder();
|
||||
$phpUnit = $finder->find('phpunit');
|
||||
|
||||
if (null === $phpUnit) {
|
||||
$this->markTestSkipped('Unable to detect phpunit binary, skipping');
|
||||
}
|
||||
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$factory->useBinary($phpUnit);
|
||||
$this->assertEquals($phpUnit, $factory->getBinary());
|
||||
}
|
||||
|
||||
public function testUseNonExistantBinary()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$factory->useBinary('itissureitdoesnotexist');
|
||||
}
|
||||
|
||||
public function testCreateShouldReturnAProcess()
|
||||
{
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$process = $factory->create();
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
|
||||
$this->assertEquals("'" . static::$phpBinary . "'", $process->getCommandLine());
|
||||
}
|
||||
|
||||
public function testCreateWithStringArgument()
|
||||
{
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$process = $factory->create('-v');
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
|
||||
$this->assertEquals("'" . static::$phpBinary . "' '-v'", $process->getCommandLine());
|
||||
}
|
||||
|
||||
public function testCreateWithArrayArgument()
|
||||
{
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$process = $factory->create(['-r', 'echo "Hello !";']);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
|
||||
$this->assertEquals("'" . static::$phpBinary . "' '-r' 'echo \"Hello !\";'", $process->getCommandLine());
|
||||
}
|
||||
|
||||
public function testCreateWithTimeout()
|
||||
{
|
||||
$factory = $this->getProcessBuilderFactory(static::$phpBinary);
|
||||
$factory->setTimeout(200);
|
||||
$process = $factory->create(['-i']);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Process\Process', $process);
|
||||
$this->assertEquals(200, $process->getTimeout());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ConfigurationTest extends TestCase
|
||||
{
|
||||
public function testArrayAccessImplementation()
|
||||
{
|
||||
$configuration = new Configuration(['key' => 'value']);
|
||||
|
||||
$this->assertTrue(isset($configuration['key']));
|
||||
$this->assertEquals('value', $configuration['key']);
|
||||
|
||||
$this->assertFalse(isset($configuration['key2']));
|
||||
unset($configuration['key']);
|
||||
$this->assertFalse(isset($configuration['key']));
|
||||
|
||||
$configuration['key2'] = 'value2';
|
||||
$this->assertTrue(isset($configuration['key2']));
|
||||
$this->assertEquals('value2', $configuration['key2']);
|
||||
}
|
||||
|
||||
public function testGetOnNonExistentKeyShouldReturnDefaultValue()
|
||||
{
|
||||
$conf = new Configuration();
|
||||
$this->assertEquals('booba', $conf->get('hooba', 'booba'));
|
||||
$this->assertEquals(null, $conf->get('hooba'));
|
||||
}
|
||||
|
||||
public function testSetHasGetRemove()
|
||||
{
|
||||
$configuration = new Configuration(['key' => 'value']);
|
||||
|
||||
$this->assertTrue($configuration->has('key'));
|
||||
$this->assertEquals('value', $configuration->get('key'));
|
||||
|
||||
$this->assertFalse($configuration->has('key2'));
|
||||
$configuration->remove('key');
|
||||
$this->assertFalse($configuration->has('key'));
|
||||
|
||||
$configuration->set('key2', 'value2');
|
||||
$this->assertTrue($configuration->has('key2'));
|
||||
$this->assertEquals('value2', $configuration->get('key2'));
|
||||
}
|
||||
|
||||
public function testIterator()
|
||||
{
|
||||
$data = [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key3' => 'value3',
|
||||
];
|
||||
|
||||
$captured = [];
|
||||
$conf = new Configuration($data);
|
||||
|
||||
foreach ($conf as $key => $value) {
|
||||
$captured[$key] = $value;
|
||||
}
|
||||
|
||||
$this->assertEquals($data, $captured);
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$data = [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key3' => 'value3',
|
||||
];
|
||||
|
||||
$conf = new Configuration($data);
|
||||
$this->assertEquals($data, $conf->all());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver\Exceptions;
|
||||
|
||||
use Alchemy\BinaryDriver\BinaryDriverTestCase;
|
||||
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
|
||||
use Alchemy\BinaryDriver\ProcessRunner;
|
||||
|
||||
class ExecutionFailureExceptionTest extends BinaryDriverTestCase
|
||||
{
|
||||
public function getProcessRunner($logger)
|
||||
{
|
||||
return new ProcessRunner($logger, 'test-runner');
|
||||
}
|
||||
|
||||
public function testGetExceptionInfo(){
|
||||
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$process = $this->createProcessMock(1, false, '--helloworld--', null, "Error Output", true);
|
||||
try{
|
||||
$runner->run($process, new \SplObjectStorage(), false);
|
||||
$this->fail('An exception should have been raised');
|
||||
}
|
||||
catch (ExecutionFailureException $e){
|
||||
$this->assertEquals("--helloworld--", $e->getCommand());
|
||||
$this->assertEquals("Error Output", $e->getErrorOutput());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\ProcessBuilderFactory;
|
||||
use LogicException;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Process\ProcessBuilder;
|
||||
|
||||
class LTSProcessBuilder extends ProcessBuilder
|
||||
{
|
||||
private $arguments;
|
||||
private $prefix;
|
||||
private $timeout;
|
||||
|
||||
public function __construct(array $arguments = array())
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
parent::__construct($arguments);
|
||||
}
|
||||
|
||||
public function setArguments(array $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPrefix($prefix)
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
$this->timeout = $timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProcess()
|
||||
{
|
||||
if (!$this->prefix && !count($this->arguments)) {
|
||||
throw new LogicException('You must add() command arguments before calling getProcess().');
|
||||
}
|
||||
|
||||
$args = $this->prefix ? array_merge(array($this->prefix), $this->arguments) : $this->arguments;
|
||||
$script = implode(' ', array_map('escapeshellarg', $args));
|
||||
|
||||
return new Process($script, null, null, null, $this->timeout);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\ProcessBuilderFactory;
|
||||
|
||||
class LTSProcessBuilderFactoryTest extends AbstractProcessBuilderFactoryTest
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
if (!class_exists('Symfony\Component\Process\ProcessBuilder')) {
|
||||
$this->markTestSkipped('ProcessBuilder is not available.');
|
||||
return;
|
||||
}
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
protected function getProcessBuilderFactory($binary)
|
||||
{
|
||||
$factory = new ProcessBuilderFactory($binary);
|
||||
$factory->setBuilder(new LTSProcessBuilder());
|
||||
ProcessBuilderFactory::$emulateSfLTS = false;
|
||||
$factory->useBinary($binary);
|
||||
|
||||
return $factory;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver\Listeners;
|
||||
|
||||
use Alchemy\BinaryDriver\Listeners\DebugListener;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class DebugListenerTest extends TestCase
|
||||
{
|
||||
public function testHandle()
|
||||
{
|
||||
$listener = new DebugListener();
|
||||
|
||||
$lines = [];
|
||||
$listener->on('debug', function ($line) use (&$lines) {
|
||||
$lines[] = $line;
|
||||
});
|
||||
$listener->handle(Process::ERR, "first line\nsecond line");
|
||||
$listener->handle(Process::OUT, "cool output");
|
||||
$listener->handle('unknown', "lalala");
|
||||
$listener->handle(Process::OUT, "another output\n");
|
||||
|
||||
$expected = [
|
||||
'[ERROR] first line',
|
||||
'[ERROR] second line',
|
||||
'[OUT] cool output',
|
||||
'[OUT] another output',
|
||||
'[OUT] ',
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $lines);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver\Listeners;
|
||||
|
||||
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
|
||||
use Alchemy\BinaryDriver\Listeners\Listeners;
|
||||
use Evenement\EventEmitter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ListenersTest extends TestCase
|
||||
{
|
||||
public function testRegister()
|
||||
{
|
||||
$listener = new MockListener();
|
||||
|
||||
$listeners = new Listeners();
|
||||
$listeners->register($listener);
|
||||
|
||||
$n = 0;
|
||||
$listener->on('received', function ($type, $data) use (&$n, &$capturedType, &$capturedData) {
|
||||
$n++;
|
||||
$capturedData = $data;
|
||||
$capturedType = $type;
|
||||
});
|
||||
|
||||
$type = 'type';
|
||||
$data = 'data';
|
||||
|
||||
$listener->handle($type, $data);
|
||||
$listener->handle($type, $data);
|
||||
|
||||
$listeners->unregister($listener);
|
||||
|
||||
$listener->handle($type, $data);
|
||||
|
||||
$this->assertEquals(3, $n);
|
||||
$this->assertEquals($type, $capturedType);
|
||||
$this->assertEquals($data, $capturedData);
|
||||
}
|
||||
|
||||
public function testRegisterAndForwardThenUnregister()
|
||||
{
|
||||
$listener = new MockListener();
|
||||
$target = new EventEmitter();
|
||||
|
||||
$n = 0;
|
||||
$target->on('received', function ($type, $data) use (&$n, &$capturedType, &$capturedData) {
|
||||
$n++;
|
||||
$capturedData = $data;
|
||||
$capturedType = $type;
|
||||
});
|
||||
|
||||
$m = 0;
|
||||
$listener->on('received', function ($type, $data) use (&$m, &$capturedType2, &$capturedData2) {
|
||||
$m++;
|
||||
$capturedData2 = $data;
|
||||
$capturedType2 = $type;
|
||||
});
|
||||
|
||||
$listeners = new Listeners();
|
||||
$listeners->register($listener, $target);
|
||||
|
||||
$type = 'type';
|
||||
$data = 'data';
|
||||
|
||||
$listener->handle($type, $data);
|
||||
$listener->handle($type, $data);
|
||||
|
||||
$listeners->unregister($listener, $target);
|
||||
|
||||
$listener->handle($type, $data);
|
||||
|
||||
$this->assertEquals(2, $n);
|
||||
$this->assertEquals(3, $m);
|
||||
$this->assertEquals($type, $capturedType);
|
||||
$this->assertEquals($data, $capturedData);
|
||||
$this->assertEquals($type, $capturedType2);
|
||||
$this->assertEquals($data, $capturedData2);
|
||||
}
|
||||
}
|
||||
|
||||
class MockListener extends EventEmitter implements ListenerInterface
|
||||
{
|
||||
public function handle($type, $data)
|
||||
{
|
||||
$this->emit('received', [$type, $data]);
|
||||
}
|
||||
|
||||
public function forwardedEvents()
|
||||
{
|
||||
return ['received'];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\ProcessBuilderFactory;
|
||||
|
||||
class NONLTSProcessBuilderFactoryTest extends AbstractProcessBuilderFactoryTest
|
||||
{
|
||||
protected function getProcessBuilderFactory($binary)
|
||||
{
|
||||
ProcessBuilderFactory::$emulateSfLTS = true;
|
||||
|
||||
return new ProcessBuilderFactory($binary);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Alchemy\BinaryDriver.
|
||||
*
|
||||
* (c) Alchemy <info@alchemy.fr>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Tests\BinaryDriver;
|
||||
|
||||
use Alchemy\BinaryDriver\ProcessRunner;
|
||||
use Alchemy\BinaryDriver\BinaryDriverTestCase;
|
||||
use Alchemy\BinaryDriver\Exception\ExecutionFailureException;
|
||||
use Alchemy\BinaryDriver\Listeners\ListenerInterface;
|
||||
use Evenement\EventEmitter;
|
||||
use Symfony\Component\Process\Exception\RuntimeException as ProcessRuntimeException;
|
||||
|
||||
class ProcessRunnerTest extends BinaryDriverTestCase
|
||||
{
|
||||
public function getProcessRunner($logger)
|
||||
{
|
||||
return new ProcessRunner($logger, 'test-runner');
|
||||
}
|
||||
|
||||
public function testRunSuccessFullProcess()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true);
|
||||
|
||||
$logger
|
||||
->expects($this->never())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->exactly(2))
|
||||
->method('info');
|
||||
|
||||
$this->assertEquals('Kikoo Romain', $runner->run($process, new \SplObjectStorage(), false));
|
||||
}
|
||||
|
||||
public function testRunSuccessFullProcessBypassingErrors()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true);
|
||||
|
||||
$logger
|
||||
->expects($this->never())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->exactly(2))
|
||||
->method('info');
|
||||
|
||||
$this->assertEquals('Kikoo Romain', $runner->run($process, new \SplObjectStorage(), true));
|
||||
}
|
||||
|
||||
public function testRunFailingProcess()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$process = $this->createProcessMock(1, false, '--helloworld--', null, null, true);
|
||||
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('info');
|
||||
|
||||
try {
|
||||
$runner->run($process, new \SplObjectStorage(), false);
|
||||
$this->fail('An exception should have been raised');
|
||||
} catch (ExecutionFailureException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testRunFailingProcessWithException()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$exception = new ProcessRuntimeException('Process Failed');
|
||||
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$process->expects($this->once())
|
||||
->method('run')
|
||||
->will($this->throwException($exception));
|
||||
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('info');
|
||||
|
||||
try {
|
||||
$runner->run($process, new \SplObjectStorage(), false);
|
||||
$this->fail('An exception should have been raised');
|
||||
} catch (ExecutionFailureException $e) {
|
||||
$this->assertEquals($exception, $e->getPrevious());
|
||||
}
|
||||
}
|
||||
|
||||
public function testRunfailingProcessBypassingErrors()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$process = $this->createProcessMock(1, false, '--helloworld--', 'Hello output', null, true);
|
||||
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('info');
|
||||
|
||||
$this->assertNull($runner->run($process, new \SplObjectStorage(), true));
|
||||
}
|
||||
|
||||
public function testRunFailingProcessWithExceptionBypassingErrors()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$exception = new ProcessRuntimeException('Process Failed');
|
||||
$process = $this->getMockBuilder('Symfony\Component\Process\Process')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$process->expects($this->once())
|
||||
->method('run')
|
||||
->will($this->throwException($exception));
|
||||
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('info');
|
||||
|
||||
$this->assertNull($runner->run($process, new \SplObjectStorage(), true));
|
||||
}
|
||||
|
||||
public function testRunSuccessFullProcessWithHandlers()
|
||||
{
|
||||
$logger = $this->createLoggerMock();
|
||||
$runner = $this->getProcessRunner($logger);
|
||||
|
||||
$capturedCallback = null;
|
||||
|
||||
$process = $this->createProcessMock(1, true, '--helloworld--', "Kikoo Romain", null, true);
|
||||
$process->expects($this->once())
|
||||
->method('run')
|
||||
->with($this->isInstanceOf('Closure'))
|
||||
->will($this->returnCallback(function ($callback) use (&$capturedCallback) {
|
||||
$capturedCallback = $callback;
|
||||
}));
|
||||
|
||||
$logger
|
||||
->expects($this->never())
|
||||
->method('error');
|
||||
$logger
|
||||
->expects($this->exactly(2))
|
||||
->method('info');
|
||||
|
||||
$listener = new TestListener();
|
||||
$storage = new \SplObjectStorage();
|
||||
$storage->attach($listener);
|
||||
|
||||
$capturedType = $capturedData = null;
|
||||
|
||||
$listener->on('received', function ($type, $data) use (&$capturedType, &$capturedData) {
|
||||
$capturedData = $data;
|
||||
$capturedType = $type;
|
||||
});
|
||||
|
||||
$this->assertEquals('Kikoo Romain', $runner->run($process, $storage, false));
|
||||
|
||||
$type = 'err';
|
||||
$data = 'data';
|
||||
|
||||
$capturedCallback($type, $data);
|
||||
|
||||
$this->assertEquals($data, $capturedData);
|
||||
$this->assertEquals($type, $capturedType);
|
||||
}
|
||||
}
|
||||
|
||||
class TestListener extends EventEmitter implements ListenerInterface
|
||||
{
|
||||
public function handle($type, $data)
|
||||
{
|
||||
return $this->emit('received', array($type, $data));
|
||||
}
|
||||
|
||||
public function forwardedEvents()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
5
tests/ExampleTest.php
Normal file
5
tests/ExampleTest.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
it('can test', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class BaseTestCase extends TestCase
|
||||
{
|
||||
public function assertScalar($value, $message = '')
|
||||
{
|
||||
$this->assertTrue(is_scalar($value), $message);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\Filters\AdvancedMedia\TestSrcFilter;
|
||||
use FFMpeg\Filters\AdvancedMedia\XStackFilter;
|
||||
use FFMpeg\Format\Audio\Mp3;
|
||||
use FFMpeg\Format\Video\X264;
|
||||
|
||||
class AdvancedMediaTest extends FunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* Path prefix to avoid conflicts with another tests.
|
||||
*/
|
||||
public const OUTPUT_PATH_PREFIX = 'output/advanced_media_';
|
||||
|
||||
public function testRunWithoutComplexFilterTestExtractAudio()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$format = new Mp3();
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'extracted_with_map.mp3';
|
||||
|
||||
// You can run it without -filter_complex, just using -map.
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia
|
||||
->map(['0:a'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'MP2/3 (MPEG audio layer 2/3)',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testAudio()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Audio.mp3')];
|
||||
$format = new Mp3();
|
||||
$format->setAudioKiloBitrate(30);
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'audio_test.mp3';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia
|
||||
->map(['0:a'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'MP2/3 (MPEG audio layer 2/3)',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testMultipleInputs()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [
|
||||
realpath(__DIR__.'/../files/portrait.MOV'),
|
||||
realpath(__DIR__.'/../files/portrait.MOV'),
|
||||
];
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'multiple_inputs_test.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->custom('[0:v][1:v]', 'hstack', '[v]');
|
||||
$advancedMedia
|
||||
->map(['0:a', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FFMpeg\Media\AdvancedMedia::map
|
||||
*/
|
||||
public function testMultipleOutputsTestAbsenceOfInputs()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
// in this test we use only computed inputs
|
||||
// and can ignore -i part of the command, pass empty inputs array.
|
||||
$inputs = [];
|
||||
$formatX264 = new X264('aac', 'libx264');
|
||||
$formatMp3 = new Mp3();
|
||||
|
||||
$outputMp3 = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_multiple_outputs.mp3';
|
||||
$outputVideo1 = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_multiple_outputs_v1.mp4';
|
||||
$outputVideo2 = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_multiple_outputs_v2.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->sine('[a]', 5)
|
||||
->testSrc('[v1]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->testSrc('[v2]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->custom('[v1]', 'negate', '[v1negate]')
|
||||
->custom('[v2]', 'edgedetect', '[v2edgedetect]');
|
||||
$advancedMedia
|
||||
->map(['[a]'], $formatMp3, $outputMp3)
|
||||
->map(['[v1negate]'], $formatX264, $outputVideo1)
|
||||
->map(['[v2edgedetect]'], $formatX264, $outputVideo2)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($outputMp3);
|
||||
$this->assertEquals(
|
||||
'MP2/3 (MPEG audio layer 2/3)',
|
||||
$ffmpeg->open($outputMp3)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($outputMp3);
|
||||
|
||||
$this->assertFileExists($outputVideo1);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($outputVideo1)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($outputVideo1);
|
||||
|
||||
$this->assertFileExists($outputVideo2);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($outputVideo2)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($outputVideo2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\TestSrcFilter
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\SineFilter
|
||||
*/
|
||||
public function testTestSrcFilterTestSineFilter()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'testsrc.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->sine('[a]', 10)
|
||||
->testSrc('[v]', TestSrcFilter::TESTSRC, '160x120', 10);
|
||||
$advancedMedia
|
||||
->map(['[a]', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* XStack filter is supported starting from 4.1 ffmpeg version.
|
||||
*
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\XStackFilter
|
||||
* @covers \FFMpeg\Filters\AdvancedMedia\SineFilter
|
||||
*/
|
||||
public function testXStackFilter()
|
||||
{
|
||||
$xStack = new XStackFilter('', 0);
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$ffmpegVersion = $ffmpeg->getFFMpegDriver()->getVersion();
|
||||
if (version_compare($ffmpegVersion, $xStack->getMinimalFFMpegVersion(), '<')) {
|
||||
$this->markTestSkipped('XStack filter is supported starting from ffmpeg version '
|
||||
.$xStack->getMinimalFFMpegVersion().', your version is '
|
||||
.$ffmpegVersion);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'xstack_test.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
->sine('[a]', 5)
|
||||
->testSrc('[v1]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->testSrc('[v2]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->testSrc('[v3]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->testSrc('[v4]', TestSrcFilter::TESTSRC, '160x120', 5)
|
||||
->xStack(
|
||||
'[v1][v2][v3][v4]',
|
||||
XStackFilter::LAYOUT_2X2,
|
||||
4,
|
||||
'[v]'
|
||||
);
|
||||
$advancedMedia
|
||||
->map(['[a]', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testOfCompatibilityWithExistedFilters()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$inputs = [realpath(__DIR__.'/../files/Test.ogv')];
|
||||
$watermark = realpath(__DIR__.'/../files/watermark.png');
|
||||
$format = new X264('aac', 'libx264');
|
||||
$output = __DIR__.'/'.self::OUTPUT_PATH_PREFIX.'test_of_compatibility_with_existed_filters.mp4';
|
||||
|
||||
$advancedMedia = $ffmpeg->openAdvanced($inputs);
|
||||
$advancedMedia->filters()
|
||||
// For unknown reasons WatermarkFilter produce an error on Windows,
|
||||
// because the path to the watermark becomes corrupted.
|
||||
// This behaviour related with Alchemy\BinaryDriver\AbstractBinary::command().
|
||||
// The path inside filter becomes like
|
||||
// "D:ServerswwwPHP-FFMpegtestsfileswatermark.png" (without slashes).
|
||||
// But on Linux systems filter works as expected.
|
||||
//->watermark('[0:v]', $watermark, '[v]')
|
||||
->pad('[0:v]', new Dimension(300, 100), '[v]');
|
||||
$advancedMedia
|
||||
->map(['0:a', '[v]'], $format, $output)
|
||||
->save();
|
||||
|
||||
$this->assertFileExists($output);
|
||||
$this->assertEquals(
|
||||
'QuickTime / MOV',
|
||||
$ffmpeg->open($output)->getFormat()->get('format_long_name')
|
||||
);
|
||||
unlink($output);
|
||||
}
|
||||
|
||||
public function testForceDisableAudio()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$format = new X264();
|
||||
|
||||
$advancedMedia1 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia1
|
||||
->map(['test'], $format, 'outputFile.mp4', false);
|
||||
$this->assertStringContainsString('acodec', $advancedMedia1->getFinalCommand());
|
||||
|
||||
$advancedMedia2 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia2
|
||||
->map(['test'], $format, 'outputFile.mp4', true);
|
||||
$this->assertStringNotContainsString('acodec', $advancedMedia2->getFinalCommand());
|
||||
}
|
||||
|
||||
public function testForceDisableVideo()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$format = new X264();
|
||||
|
||||
$advancedMedia1 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia1->map(
|
||||
['test'],
|
||||
$format,
|
||||
'outputFile.mp4',
|
||||
false,
|
||||
false
|
||||
);
|
||||
$this->assertStringContainsString('vcodec', $advancedMedia1->getFinalCommand());
|
||||
|
||||
$advancedMedia2 = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$advancedMedia2->map(
|
||||
['test'],
|
||||
$format,
|
||||
'outputFile.mp4',
|
||||
false,
|
||||
true
|
||||
);
|
||||
$this->assertStringNotContainsString('vcodec', $advancedMedia2->getFinalCommand());
|
||||
}
|
||||
|
||||
public function testGlobalOptions()
|
||||
{
|
||||
$configuration = [
|
||||
'ffmpeg.threads' => 3,
|
||||
'ffmpeg.filter_threads' => 13,
|
||||
'ffmpeg.filter_complex_threads' => 24,
|
||||
];
|
||||
|
||||
$ffmpeg = $this->getFFMpeg($configuration);
|
||||
$advancedMedia = $ffmpeg->openAdvanced([__FILE__]);
|
||||
$command = $advancedMedia->getFinalCommand();
|
||||
|
||||
foreach ($configuration as $optionName => $optionValue) {
|
||||
$optionName = str_replace('ffmpeg.', '', $optionName);
|
||||
$this->assertStringContainsString('-'.$optionName.' '.$optionValue, $command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
class AudioConcatenationTest extends FunctionalTestCase
|
||||
{
|
||||
public function testSimpleAudioFileConcatTest()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
|
||||
$files = [
|
||||
realpath(__DIR__ . '/../files/Jahzzar_-_05_-_Siesta.mp3'),
|
||||
realpath(__DIR__ . '/../files/02_-_Favorite_Secrets.mp3'),
|
||||
];
|
||||
|
||||
$audio = $ffmpeg->open(reset($files));
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\Media\Audio', $audio);
|
||||
|
||||
clearstatcache();
|
||||
$filename = __DIR__ . '/output/concat-output.mp3';
|
||||
|
||||
$audio->concat($files)->saveFromSameCodecs($filename, true);
|
||||
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\FFProbe;
|
||||
|
||||
class FFProbeTest extends FunctionalTestCase
|
||||
{
|
||||
public function testProbeOnFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertGreaterThan(0, count($ffprobe->streams(__DIR__.'/../files/Audio.mp3')));
|
||||
}
|
||||
|
||||
public function testValidateExistingFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertTrue($ffprobe->isValid(__DIR__.'/../files/sample.3gp'));
|
||||
}
|
||||
|
||||
public function testValidateNonExistingFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertFalse($ffprobe->isValid(__DIR__.'/../files/WrongFile.mp4'));
|
||||
}
|
||||
|
||||
public function testProbeOnNonExistantFile()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
|
||||
$ffprobe = FFProbe::create();
|
||||
$ffprobe->streams('/path/to/no/file');
|
||||
}
|
||||
|
||||
public function testProbeOnRemoteFile()
|
||||
{
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertGreaterThan(0, count($ffprobe->streams('http://vjs.zencdn.net/v/oceans.mp4')));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\FFMpeg;
|
||||
use Tests\FFMpeg\BaseTestCase;
|
||||
|
||||
abstract class FunctionalTestCase extends BaseTestCase
|
||||
{
|
||||
/**
|
||||
* @param array $configuration
|
||||
*
|
||||
* @return FFMpeg
|
||||
*/
|
||||
public function getFFMpeg($configuration = [])
|
||||
{
|
||||
return FFMpeg::create(array_merge(['timeout' => 300], $configuration));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Functional;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\Filters\Video\ResizeFilter;
|
||||
use FFMpeg\Filters\Video\RotateFilter;
|
||||
use FFMpeg\Format\Video\X264;
|
||||
use FFMpeg\Media\Video;
|
||||
|
||||
class VideoTranscodeTest extends FunctionalTestCase
|
||||
{
|
||||
public function testSimpleTranscodeX264()
|
||||
{
|
||||
$filename = __DIR__.'/output/output-x264.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__.'/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__.'/../files/Test.ogv');
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\Media\Video', $video);
|
||||
|
||||
$lastPercentage = null;
|
||||
$phpunit = $this;
|
||||
|
||||
$codec = new X264('aac');
|
||||
$codec->on('progress', function ($video, $codec, $percentage) use ($phpunit, &$lastPercentage) {
|
||||
if (null !== $lastPercentage) {
|
||||
$phpunit->assertGreaterThanOrEqual($lastPercentage, $percentage);
|
||||
}
|
||||
$lastPercentage = $percentage;
|
||||
$phpunit->assertGreaterThanOrEqual(0, $percentage);
|
||||
$phpunit->assertLessThanOrEqual(100, $percentage);
|
||||
});
|
||||
|
||||
$video->save($codec, $filename);
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
public function testAacTranscodeX264()
|
||||
{
|
||||
$filename = __DIR__.'/output/output-x264_2.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__.'/output/output-x264_2.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__.'/../files/sample.3gp');
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\Media\Video', $video);
|
||||
|
||||
$lastPercentage = null;
|
||||
$phpunit = $this;
|
||||
|
||||
$codec = new X264('aac');
|
||||
$codec->on('progress', function ($video, $codec, $percentage) use ($phpunit, &$lastPercentage) {
|
||||
if (null !== $lastPercentage) {
|
||||
$phpunit->assertGreaterThanOrEqual($lastPercentage, $percentage);
|
||||
}
|
||||
$lastPercentage = $percentage;
|
||||
$phpunit->assertGreaterThanOrEqual(0, $percentage);
|
||||
$phpunit->assertLessThanOrEqual(100, $percentage);
|
||||
});
|
||||
|
||||
$video->save($codec, $filename);
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
public function testTranscodeInvalidFile()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$ffmpeg->open(__DIR__.'/../files/UnknownFileTest.ogv');
|
||||
}
|
||||
|
||||
public function testSaveInvalidForgedVideo()
|
||||
{
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = new Video(__DIR__.'/../files/UnknownFileTest.ogv', $ffmpeg->getFFMpegDriver(), $ffmpeg->getFFProbe());
|
||||
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
$video->save(new X264('aac'), __DIR__.'/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
public function testTranscodePortraitVideo()
|
||||
{
|
||||
$info = $this->getNameAndVersion();
|
||||
|
||||
if ('avconv' === $info['name'] && version_compare($info['version'], '0.9', '<')) {
|
||||
$this->markTestSkipped('This version of avconv is buggy and does not support this test.');
|
||||
}
|
||||
|
||||
$filename = __DIR__.'/output/output-x264.mp4';
|
||||
if (is_file($filename)) {
|
||||
unlink(__DIR__.'/output/output-x264.mp4');
|
||||
}
|
||||
|
||||
$ffmpeg = $this->getFFMpeg();
|
||||
$video = $ffmpeg->open(__DIR__.'/../files/portrait.MOV');
|
||||
|
||||
$video->filters()
|
||||
->resize(new Dimension(320, 240), ResizeFilter::RESIZEMODE_INSET)
|
||||
->rotate(RotateFilter::ROTATE_90);
|
||||
$video->save(new X264('aac'), $filename);
|
||||
|
||||
$dimension = $ffmpeg->getFFProbe()
|
||||
->streams($filename)
|
||||
->videos()
|
||||
->first()
|
||||
->getDimensions();
|
||||
|
||||
$this->assertLessThan(1, $dimension->getRatio(false)->getValue());
|
||||
$this->assertEquals(240, $dimension->getHeight());
|
||||
|
||||
$this->assertFileExists($filename);
|
||||
unlink($filename);
|
||||
}
|
||||
|
||||
private function getNameAndVersion()
|
||||
{
|
||||
$binary = $this
|
||||
->getFFMpeg()
|
||||
->getFFMpegDriver()
|
||||
->getProcessBuilderFactory()
|
||||
->getBinary();
|
||||
|
||||
$output = $matches = null;
|
||||
exec($binary.' -version 2>&1', $output);
|
||||
|
||||
if (!isset($output[0])) {
|
||||
return ['name' => null, 'version' => null];
|
||||
}
|
||||
|
||||
preg_match('/^([a-z]+)\s+version\s+([0-9\.]+)/i', $output[0], $matches);
|
||||
|
||||
if (count($matches) > 0) {
|
||||
return ['name' => $matches[1], 'version' => $matches[2]];
|
||||
}
|
||||
|
||||
return ['name' => null, 'version' => null];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Coordinate;
|
||||
|
||||
use FFMpeg\Coordinate\AspectRatio;
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AspectRatioTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideDimensionsAndExpectedratio
|
||||
*/
|
||||
public function testFromDimensions($width, $height, $strategy, $expected, $calculatedWidth, $calculatedHeight, $modulus = 2)
|
||||
{
|
||||
$ratio = AspectRatio::create(new Dimension($width, $height), $strategy);
|
||||
$this->assertEquals($expected, $ratio->getValue());
|
||||
|
||||
$this->assertEquals($calculatedHeight, $ratio->calculateHeight(240, $modulus));
|
||||
$this->assertEquals($calculatedWidth, $ratio->calculateWidth(320, $modulus));
|
||||
}
|
||||
|
||||
public function provideDimensionsAndExpectedratio()
|
||||
{
|
||||
return [
|
||||
//AR_5_4
|
||||
[720, 576, false, 5 / 4, 400, 192],
|
||||
[720, 577, false, 5 / 4, 400, 192],
|
||||
[720, 620, false, 720 / 620, 372, 206],
|
||||
[720, 576, true, 5 / 4, 400, 192],
|
||||
//AR_ROTATED_4_5
|
||||
[576, 720, false, 4 / 5, 256, 300],
|
||||
[576, 720, true, 4 / 5, 256, 300],
|
||||
//AR_4_3
|
||||
[320, 240, false, 4 / 3, 426, 180],
|
||||
[320, 240, true, 4 / 3, 426, 180],
|
||||
//AR_ROTATED_3_4
|
||||
[240, 320, false, 3 / 4, 240, 320],
|
||||
[240, 320, true, 3 / 4, 240, 320],
|
||||
//AR_16_9
|
||||
[1920, 1080, false, 16 / 9, 568, 136],
|
||||
[1920, 1080, true, 16 / 9, 568, 136],
|
||||
[1280, 720, false, 16 / 9, 568, 136],
|
||||
[1280, 720, true, 16 / 9, 568, 136],
|
||||
[3840, 2160, false, 16 / 9, 568, 136],
|
||||
[3840, 2160, true, 16 / 9, 568, 136],
|
||||
// modulus 4
|
||||
[1920, 1080, false, 16 / 9, 568, 136, 4],
|
||||
[1920, 1080, true, 16 / 9, 568, 136, 4],
|
||||
[1280, 720, false, 16 / 9, 568, 136, 4],
|
||||
[1280, 720, true, 16 / 9, 568, 136, 4],
|
||||
[3840, 2160, false, 16 / 9, 568, 136, 4],
|
||||
[3840, 2160, true, 16 / 9, 568, 136, 4],
|
||||
// modulus 16
|
||||
[1920, 1080, false, 16 / 9, 576, 128, 16],
|
||||
[1920, 1080, true, 16 / 9, 576, 128, 16],
|
||||
[1280, 720, false, 16 / 9, 576, 128, 16],
|
||||
[1280, 720, true, 16 / 9, 576, 128, 16],
|
||||
[3840, 2160, false, 16 / 9, 576, 128, 16],
|
||||
[3840, 2160, true, 16 / 9, 576, 128, 16],
|
||||
//AR_ROTATED_9_16
|
||||
[1080, 1920, false, 9 / 16, 180, 426],
|
||||
[1080, 1920, true, 9 / 16, 180, 426],
|
||||
[720, 1280, false, 9 / 16, 180, 426],
|
||||
[720, 1280, true, 9 / 16, 180, 426],
|
||||
[2160, 3840, false, 9 / 16, 180, 426],
|
||||
[2160, 3840, true, 9 / 16, 180, 426],
|
||||
//AR_3_2
|
||||
[360, 240, false, 3 / 2, 480, 160],
|
||||
[360, 240, true, 3 / 2, 480, 160],
|
||||
//AR_ROTATED_2_3
|
||||
[240, 360, false, 2 / 3, 214, 360],
|
||||
[240, 360, true, 2 / 3, 214, 360],
|
||||
//AR_5_3
|
||||
//AR_ROTATED_3_5
|
||||
//AR_1_1
|
||||
//AR_1_DOT_85_1
|
||||
//AR_ROTATED_1_DOT_85
|
||||
//AR_2_DOT_39_1
|
||||
//AR_ROTATED_2_DOT_39
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Coordinate;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class DimensionTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideInvalidDimensions
|
||||
*/
|
||||
public function testInvalidDimensions($width, $height)
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
new Dimension($width, $height);
|
||||
}
|
||||
|
||||
public function provideInvalidDimensions()
|
||||
{
|
||||
return [
|
||||
[320, 0],
|
||||
[320, -10],
|
||||
[0, 240],
|
||||
[-10, 240],
|
||||
[0, 0],
|
||||
[0, -10],
|
||||
[-10, 0],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetters()
|
||||
{
|
||||
$dimension = new Dimension(320, 240);
|
||||
$this->assertEquals(320, $dimension->getWidth());
|
||||
$this->assertEquals(240, $dimension->getHeight());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Coordinate;
|
||||
|
||||
use FFMpeg\Coordinate\FrameRate;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class FrameRateTest extends TestCase
|
||||
{
|
||||
public function testGetter()
|
||||
{
|
||||
$fr = new FrameRate(23.997);
|
||||
$this->assertEquals(23.997, $fr->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidFrameRates
|
||||
*/
|
||||
public function testInvalidFrameRate($value)
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
new FrameRate($value);
|
||||
}
|
||||
|
||||
public function provideInvalidFrameRates()
|
||||
{
|
||||
return [
|
||||
[0], [-1.5], [-2],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Coordinate;
|
||||
|
||||
use FFMpeg\Coordinate\Point;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class PointTest extends TestCase
|
||||
{
|
||||
public function testGetters()
|
||||
{
|
||||
$point = new Point(4, 25);
|
||||
$this->assertEquals(4, $point->getX());
|
||||
$this->assertEquals(25, $point->getY());
|
||||
}
|
||||
|
||||
public function testDynamicPointGetters()
|
||||
{
|
||||
$point = new Point('t*100', 't', true);
|
||||
$this->assertEquals('t*100', $point->getX());
|
||||
$this->assertEquals('t', $point->getY());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Coordinate;
|
||||
|
||||
use FFMpeg\Coordinate\TimeCode;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class TimeCodeTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideTimecodes
|
||||
*/
|
||||
public function testFromString($timecode, $expected)
|
||||
{
|
||||
$tc = TimeCode::fromString($timecode);
|
||||
$this->assertEquals((string) $tc, $expected);
|
||||
}
|
||||
|
||||
public function provideTimeCodes()
|
||||
{
|
||||
return [
|
||||
['1:02:04:05:20', '26:04:05.20'],
|
||||
['1:02:04:05.20', '26:04:05.20'],
|
||||
['02:04:05:20', '02:04:05.20'],
|
||||
['02:04:05.20', '02:04:05.20'],
|
||||
['00:00:05.20', '00:00:05.20'],
|
||||
['00:00:00.00', '00:00:00.00'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testFromInvalidString()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
TimeCode::fromString('lalali lala');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSeconds
|
||||
*/
|
||||
public function testFromSeconds($seconds, $expected)
|
||||
{
|
||||
$tc = TimeCode::fromSeconds($seconds);
|
||||
$this->assertEquals($expected, (string) $tc);
|
||||
}
|
||||
|
||||
public function provideSeconds()
|
||||
{
|
||||
return [
|
||||
[0.467, '00:00:00.47'],
|
||||
[12.467, '00:00:12.47'],
|
||||
[59.867, '00:00:59.87'],
|
||||
[72.467, '00:01:12.47'],
|
||||
[3599.467, '00:59:59.47'],
|
||||
[3600.467, '01:00:00.47'],
|
||||
[86422.467, '24:00:22.47'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Driver;
|
||||
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
use FFMpeg\Driver\FFMpegDriver;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class FFMpegDriverTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
$executableFinder = new ExecutableFinder();
|
||||
|
||||
$found = false;
|
||||
foreach (['avconv', 'ffmpeg'] as $name) {
|
||||
if (null !== $executableFinder->find($name)) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$this->markTestSkipped('Neither ffmpeg or avconv found');
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
$logger = $this->getLoggerMock();
|
||||
$ffmpeg = FFMpegDriver::create($logger, []);
|
||||
$this->assertInstanceOf('FFMpeg\Driver\FFMpegDriver', $ffmpeg);
|
||||
$this->assertEquals($logger, $ffmpeg->getProcessRunner()->getLogger());
|
||||
}
|
||||
|
||||
public function testCreateWithConfig()
|
||||
{
|
||||
$conf = new Configuration();
|
||||
$ffmpeg = FFMpegDriver::create($this->getLoggerMock(), $conf);
|
||||
$this->assertEquals($conf, $ffmpeg->getConfiguration());
|
||||
}
|
||||
|
||||
public function testCreateFailureThrowsAnException()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\ExecutableNotFoundException');
|
||||
FFMpegDriver::create($this->getLoggerMock(), ['ffmpeg.binaries' => '/path/to/nowhere']);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Driver;
|
||||
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
use FFMpeg\Driver\FFProbeDriver;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class FFProbeDriverTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
$executableFinder = new ExecutableFinder();
|
||||
|
||||
$found = false;
|
||||
foreach (['avprobe', 'ffprobe'] as $name) {
|
||||
if (null !== $executableFinder->find($name)) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$this->markTestSkipped('Neither ffprobe or avprobe found');
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
$logger = $this->getLoggerMock();
|
||||
$ffprobe = FFProbeDriver::create([], $logger);
|
||||
$this->assertInstanceOf('FFMpeg\Driver\FFProbeDriver', $ffprobe);
|
||||
$this->assertEquals($logger, $ffprobe->getProcessRunner()->getLogger());
|
||||
}
|
||||
|
||||
public function testCreateWithConfig()
|
||||
{
|
||||
$conf = new Configuration();
|
||||
$ffprobe = FFProbeDriver::create($conf, $this->getLoggerMock());
|
||||
$this->assertEquals($conf, $ffprobe->getConfiguration());
|
||||
}
|
||||
|
||||
public function testCreateFailureThrowsAnException()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\ExecutableNotFoundException');
|
||||
FFProbeDriver::create(['ffprobe.binaries' => '/path/to/nowhere']);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit;
|
||||
|
||||
use FFMpeg\FFMpeg;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
|
||||
class FFMpegTest extends TestCase
|
||||
{
|
||||
public function testOpenInvalid()
|
||||
{
|
||||
$this->expectException(
|
||||
'\FFMpeg\Exception\RuntimeException',
|
||||
'Unable to probe "/path/to/unknown/file"'
|
||||
);
|
||||
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $this->getFFProbeMock());
|
||||
$ffmpeg->open('/path/to/unknown/file');
|
||||
}
|
||||
|
||||
public function testOpenAudio()
|
||||
{
|
||||
$streams = $this->getStreamCollectionMock();
|
||||
$streams->expects($this->once())
|
||||
->method('audios')
|
||||
->will($this->returnValue(new StreamCollection([new Stream([])])));
|
||||
$streams->expects($this->once())
|
||||
->method('videos')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('streams')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
|
||||
$this->assertInstanceOf('FFMpeg\Media\Audio', $ffmpeg->open(__FILE__));
|
||||
}
|
||||
|
||||
public function testOpenVideo()
|
||||
{
|
||||
$streams = $this->getStreamCollectionMock();
|
||||
$streams->expects($this->once())
|
||||
->method('videos')
|
||||
->will($this->returnValue(new StreamCollection([new Stream([])])));
|
||||
$streams->expects($this->never())
|
||||
->method('audios');
|
||||
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('streams')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
|
||||
$this->assertInstanceOf('FFMpeg\Media\Video', $ffmpeg->open(__FILE__));
|
||||
}
|
||||
|
||||
public function testOpenUnknown()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('streams')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue(new StreamCollection()));
|
||||
|
||||
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
|
||||
$ffmpeg->open(__FILE__);
|
||||
}
|
||||
|
||||
public function testCreateWithoutLoggerOrProbe()
|
||||
{
|
||||
$this->assertInstanceOf('FFMpeg\FFMpeg', FFMpeg::create());
|
||||
}
|
||||
|
||||
public function testCreateWithLoggerAndProbe()
|
||||
{
|
||||
$logger = $this->getLoggerMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$ffmpeg = FFMpeg::create(['timeout' => 42], $logger, $ffprobe);
|
||||
$this->assertInstanceOf('FFMpeg\FFMpeg', $ffmpeg);
|
||||
|
||||
$this->assertSame($logger, $ffmpeg->getFFMpegDriver()->getProcessRunner()->getLogger());
|
||||
$this->assertSame($ffprobe, $ffmpeg->getFFProbe());
|
||||
$this->assertSame(42, $ffmpeg->getFFMpegDriver()->getProcessBuilderFactory()->getTimeout());
|
||||
}
|
||||
|
||||
public function testGetSetFFProbe()
|
||||
{
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$ffmpeg = new FFMpeg($this->getFFMpegDriverMock(), $ffprobe);
|
||||
$this->assertSame($ffprobe, $ffmpeg->getFFProbe());
|
||||
$anotherFFProbe = $this->getFFProbeMock();
|
||||
$ffmpeg->setFFProbe($anotherFFProbe);
|
||||
$this->assertSame($anotherFFProbe, $ffmpeg->getFFProbe());
|
||||
}
|
||||
|
||||
public function testGetSetDriver()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffmpeg = new FFMpeg($driver, $this->getFFProbeMock());
|
||||
$this->assertSame($driver, $ffmpeg->getFFMpegDriver());
|
||||
$anotherDriver = $this->getFFMpegDriverMock();
|
||||
$ffmpeg->setFFMpegDriver($anotherDriver);
|
||||
$this->assertSame($anotherDriver, $ffmpeg->getFFMpegDriver());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\FFProbe\DataMapping;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\AbstractData;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AbstractDataTest extends TestCase
|
||||
{
|
||||
public function testHas()
|
||||
{
|
||||
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
|
||||
|
||||
$this->assertTrue($imp->has('key1'));
|
||||
$this->assertTrue($imp->has('key2'));
|
||||
$this->assertFalse($imp->has('value1'));
|
||||
$this->assertFalse($imp->has('key3'));
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
|
||||
|
||||
$this->assertEquals('value1', $imp->get('key1'));
|
||||
$this->assertEquals('value2', $imp->get('key2'));
|
||||
}
|
||||
|
||||
public function testGetDefault()
|
||||
{
|
||||
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
|
||||
$this->assertSame('yololo', $imp->get('key3', 'yololo'));
|
||||
}
|
||||
|
||||
public function testKeys()
|
||||
{
|
||||
$imp = new Implementation(['key1' => 'value1', 'key2' => 'value2']);
|
||||
|
||||
$this->assertEquals(['key1', 'key2'], $imp->keys());
|
||||
}
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$values = ['key1' => 'value1', 'key2' => 'value2'];
|
||||
$imp = new Implementation($values);
|
||||
|
||||
$this->assertEquals($values, $imp->all());
|
||||
}
|
||||
}
|
||||
|
||||
class Implementation extends AbstractData
|
||||
{
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\FFProbe\DataMapping;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class StreamCollectionTest extends TestCase
|
||||
{
|
||||
public function testAdd()
|
||||
{
|
||||
$stream = $this->getStreamMock();
|
||||
|
||||
$collection = new StreamCollection();
|
||||
$this->assertEquals([], $collection->all());
|
||||
$collection->add($stream);
|
||||
$this->assertEquals([$stream], $collection->all());
|
||||
$collection->add($stream);
|
||||
$this->assertEquals([$stream, $stream], $collection->all());
|
||||
}
|
||||
|
||||
public function testVideos()
|
||||
{
|
||||
$audio = $this->getStreamMock();
|
||||
$audio->expects($this->once())
|
||||
->method('isVideo')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$video = $this->getStreamMock();
|
||||
$video->expects($this->once())
|
||||
->method('isVideo')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$collection = new StreamCollection([$audio, $video]);
|
||||
$videos = $collection->videos();
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\FFProbe\DataMapping\StreamCollection', $videos);
|
||||
$this->assertCount(1, $videos);
|
||||
$this->assertEquals([$video], $videos->all());
|
||||
}
|
||||
|
||||
public function testAudios()
|
||||
{
|
||||
$audio = $this->getStreamMock();
|
||||
$audio->expects($this->once())
|
||||
->method('isAudio')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$video = $this->getStreamMock();
|
||||
$video->expects($this->once())
|
||||
->method('isAudio')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$collection = new StreamCollection([$audio, $video]);
|
||||
$audios = $collection->audios();
|
||||
|
||||
$this->assertInstanceOf('FFMpeg\FFProbe\DataMapping\StreamCollection', $audios);
|
||||
$this->assertCount(1, $audios);
|
||||
$this->assertEquals([$audio], $audios->all());
|
||||
}
|
||||
|
||||
public function testCount()
|
||||
{
|
||||
$stream = $this->getStreamMock();
|
||||
|
||||
$collection = new StreamCollection([$stream]);
|
||||
$this->assertCount(1, $collection);
|
||||
}
|
||||
|
||||
public function testGetIterator()
|
||||
{
|
||||
$audio = $this->getStreamMock();
|
||||
$video = $this->getStreamMock();
|
||||
|
||||
$collection = new StreamCollection([$audio, $video]);
|
||||
$this->assertInstanceOf('\Iterator', $collection->getIterator());
|
||||
$this->assertCount(2, $collection->getIterator());
|
||||
}
|
||||
|
||||
public function testFirst()
|
||||
{
|
||||
$stream1 = $this->getStreamMock();
|
||||
$stream2 = $this->getStreamMock();
|
||||
|
||||
$coll = new StreamCollection([$stream1, $stream2]);
|
||||
|
||||
$this->assertSame($stream1, $coll->first());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\FFProbe\DataMapping;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class StreamTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideAudioCases
|
||||
*/
|
||||
public function testIsAudio($isAudio, $properties)
|
||||
{
|
||||
$stream = new Stream($properties);
|
||||
$this->assertTrue($isAudio === $stream->isAudio());
|
||||
}
|
||||
|
||||
public function provideAudioCases()
|
||||
{
|
||||
return [
|
||||
[true, ['codec_type' => 'audio']],
|
||||
[false, ['codec_type' => 'video']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideVideoCases
|
||||
*/
|
||||
public function testIsVideo($isVideo, $properties)
|
||||
{
|
||||
$stream = new Stream($properties);
|
||||
$this->assertTrue($isVideo === $stream->isVideo());
|
||||
}
|
||||
|
||||
public function provideVideoCases()
|
||||
{
|
||||
return [
|
||||
[true, ['codec_type' => 'video']],
|
||||
[false, ['codec_type' => 'audio']],
|
||||
];
|
||||
}
|
||||
|
||||
public function testGetDimensionsFromAudio()
|
||||
{
|
||||
$this->expectException(
|
||||
'\FFMpeg\Exception\LogicException',
|
||||
'Dimensions can only be retrieved from video streams.'
|
||||
);
|
||||
$stream = new Stream(['codec_type' => 'audio']);
|
||||
$stream->getDimensions();
|
||||
}
|
||||
|
||||
public function testGetDimensionsFromVideo()
|
||||
{
|
||||
$stream = new Stream(['codec_type' => 'video', 'width' => 960, 'height' => 720]);
|
||||
$this->assertEquals(new Dimension(960, 720), $stream->getDimensions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidPropertiesForDimensionsExtraction
|
||||
*/
|
||||
public function testUnableToGetDimensionsFromVideo($properties)
|
||||
{
|
||||
$this->expectException(
|
||||
'\FFMpeg\Exception\RuntimeException',
|
||||
'Unable to extract dimensions.'
|
||||
);
|
||||
$stream = new Stream(['codec_type' => 'video', 'width' => 960]);
|
||||
$stream->getDimensions();
|
||||
}
|
||||
|
||||
public function provideInvalidPropertiesForDimensionsExtraction()
|
||||
{
|
||||
return [
|
||||
['codec_type' => 'video', 'width' => 960],
|
||||
['codec_type' => 'video', 'height' => 960],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providePropertiesForDimensionsExtraction
|
||||
*/
|
||||
public function testGetDimensionsFromVideoWithDisplayRatio($data)
|
||||
{
|
||||
$stream = new Stream([
|
||||
'codec_type' => 'video',
|
||||
'width' => $data['width'],
|
||||
'height' => $data['height'],
|
||||
'sample_aspect_ratio' => $data['sar'],
|
||||
'display_aspect_ratio' => $data['dar'],
|
||||
]);
|
||||
$this->assertEquals(new Dimension($data['result_width'], $data['result_height']), $stream->getDimensions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidRatios
|
||||
*/
|
||||
public function testGetDimensionsFromVideoWithInvalidDisplayRatio($invalidRatio)
|
||||
{
|
||||
$stream = new Stream(['codec_type' => 'video', 'width' => 960, 'height' => 720, 'sample_aspect_ratio' => $invalidRatio, 'display_aspect_ratio' => '16:9']);
|
||||
$this->assertEquals(new Dimension(960, 720), $stream->getDimensions());
|
||||
}
|
||||
|
||||
public function provideInvalidRatios()
|
||||
{
|
||||
return [['0:1'], ['2:1:3']];
|
||||
}
|
||||
|
||||
public function providePropertiesForDimensionsExtraction()
|
||||
{
|
||||
return [
|
||||
[
|
||||
['width' => '960', 'height' => '720',
|
||||
'sar' => '4:3', 'dar' => '16:9',
|
||||
'result_width' => '1280', 'result_height' => '720', ],
|
||||
],
|
||||
[
|
||||
['width' => '1920', 'height' => '1080',
|
||||
'sar' => '1:1', 'dar' => '16:9',
|
||||
'result_width' => '1920', 'result_height' => '1080', ],
|
||||
],
|
||||
[
|
||||
['width' => '640', 'height' => '480',
|
||||
'sar' => '75:74', 'dar' => '50:37',
|
||||
'result_width' => '649', 'result_height' => '480', ],
|
||||
],
|
||||
[
|
||||
['width' => '720', 'height' => '576',
|
||||
'sar' => '52:28', 'dar' => '16:9',
|
||||
'result_width' => '1337', 'result_height' => '752', ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\FFProbe;
|
||||
|
||||
use FFMpeg\FFProbe;
|
||||
use FFMpeg\FFProbe\DataMapping\Format;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\FFProbe\Mapper;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class MapperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideMappings
|
||||
*/
|
||||
public function testMap($type, $data, $expected)
|
||||
{
|
||||
$mapper = new Mapper();
|
||||
$this->assertEquals($expected, $mapper->map($type, $data));
|
||||
}
|
||||
|
||||
public function testMapInvalidArgument()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$mapper = new Mapper();
|
||||
$mapper->map('cool type', 'data');
|
||||
}
|
||||
|
||||
public function provideMappings()
|
||||
{
|
||||
$format = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_format.json'), true);
|
||||
$streams = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_streams.json'), true);
|
||||
|
||||
return [
|
||||
[FFProbe::TYPE_FORMAT, $format, new Format($format['format'])],
|
||||
[FFProbe::TYPE_STREAMS, $streams, new StreamCollection(array_map(function ($streamData) {
|
||||
return new Stream($streamData);
|
||||
}, $streams['streams']))],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\FFProbe;
|
||||
|
||||
use FFMpeg\FFProbe\OptionsTester;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class OptionsTesterTest extends TestCase
|
||||
{
|
||||
public function testHasOptionWithOldFFProbe()
|
||||
{
|
||||
$this->expectException(
|
||||
'\FFMpeg\Exception\RuntimeException',
|
||||
'Your FFProbe version is too old and does not support `-help` option, please upgrade.'
|
||||
);
|
||||
$cache = $this->getCacheMock();
|
||||
|
||||
$executionFailerExceptionMock = $this->getMockBuilder('Alchemy\BinaryDriver\Exception\ExecutionFailureException')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$ffprobe = $this->getFFProbeDriverMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('command')
|
||||
->with(['-help', '-loglevel', 'quiet'])
|
||||
->will($this->throwException($executionFailerExceptionMock));
|
||||
|
||||
$tester = new OptionsTester($ffprobe, $cache);
|
||||
$tester->has('-print_format');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideOptions
|
||||
*/
|
||||
public function testHasOptionWithCacheEmpty($isPresent, $data, $optionName)
|
||||
{
|
||||
$cache = $this->getCacheMock();
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('getItem')
|
||||
->will($this->returnValue(new CacheItem));
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('hasItem')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('save');
|
||||
|
||||
$ffprobe = $this->getFFProbeDriverMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('command')
|
||||
->with(['-help', '-loglevel', 'quiet'])
|
||||
->will($this->returnValue($data));
|
||||
|
||||
$tester = new OptionsTester($ffprobe, $cache);
|
||||
$this->assertTrue($isPresent === $tester->has($optionName));
|
||||
}
|
||||
|
||||
public function provideOptions()
|
||||
{
|
||||
$data = file_get_contents(__DIR__ . '/../../fixtures/ffprobe/help.raw');
|
||||
|
||||
return [
|
||||
[true, $data, '-print_format'],
|
||||
[false, $data, '-another_print_format'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideOptions
|
||||
*/
|
||||
public function testHasOptionWithHelpCacheLoaded($isPresent, $data, $optionName)
|
||||
{
|
||||
$cache = $this->getCacheMock();
|
||||
|
||||
$cacheItem = new CacheItem;
|
||||
$cacheItem->set($data);
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('getItem')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$this->returnValue($cacheItem),
|
||||
$this->returnValue(new CacheItem)
|
||||
);
|
||||
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('hasItem')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
$this->returnValue(false),
|
||||
$this->returnValue(true)
|
||||
);
|
||||
$cache->expects($this->once())
|
||||
->method('save');
|
||||
|
||||
$ffprobe = $this->getFFProbeDriverMock();
|
||||
$ffprobe->expects($this->never())
|
||||
->method('command');
|
||||
|
||||
$tester = new OptionsTester($ffprobe, $cache);
|
||||
$this->assertTrue($isPresent === $tester->has($optionName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideOptions
|
||||
*/
|
||||
public function testHasOptionWithCacheFullyLoaded($isPresent, $data, $optionName)
|
||||
{
|
||||
$cache = $this->getCacheMock();
|
||||
|
||||
$cacheItem = new CacheItem();
|
||||
$cacheItem->set($isPresent);
|
||||
|
||||
$cache->expects($this->once())
|
||||
->method('getItem')
|
||||
->with(md5('option-' . $optionName))
|
||||
->will($this->returnValue($cacheItem));
|
||||
|
||||
$cache->expects($this->once())
|
||||
->method('hasItem')
|
||||
->with(md5('option-' . $optionName))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$ffprobe = $this->getFFProbeDriverMock();
|
||||
$ffprobe->expects($this->never())
|
||||
->method('command');
|
||||
|
||||
$tester = new OptionsTester($ffprobe, $cache);
|
||||
$this->assertTrue($isPresent === $tester->has($optionName));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\FFProbe;
|
||||
|
||||
use FFMpeg\FFProbe;
|
||||
use FFMpeg\FFProbe\OutputParser;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class OutputParserTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideTypeDataAndOutput
|
||||
*/
|
||||
public function testParse($type, $data, $expectedOutput)
|
||||
{
|
||||
$parser = new OutputParser();
|
||||
$this->assertEquals($expectedOutput, $parser->parse($type, $data));
|
||||
}
|
||||
|
||||
public function testParseWithInvalidArgument()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$parser = new OutputParser();
|
||||
$parser->parse('comme ca', 'data');
|
||||
}
|
||||
|
||||
public function provideTypeDataAndOutput()
|
||||
{
|
||||
$expectedFormat = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_format.json'), true);
|
||||
$expectedStreams = json_decode(file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_streams.json'), true);
|
||||
|
||||
$rawFormat = file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_format.raw');
|
||||
$rawStreams = file_get_contents(__DIR__.'/../../fixtures/ffprobe/show_streams.raw');
|
||||
|
||||
return [
|
||||
[FFProbe::TYPE_FORMAT, $rawFormat, $expectedFormat],
|
||||
[FFProbe::TYPE_STREAMS, $rawStreams, $expectedStreams],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit;
|
||||
|
||||
use Alchemy\BinaryDriver\Configuration;
|
||||
use Alchemy\BinaryDriver\ConfigurationInterface;
|
||||
use FFMpeg\FFProbe;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
|
||||
class FFProbeTest extends TestCase
|
||||
{
|
||||
public function testGetSetParser()
|
||||
{
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
$parser = $this->getFFProbeParserMock();
|
||||
|
||||
$ffprobe->setParser($parser);
|
||||
$this->assertSame($parser, $ffprobe->getParser());
|
||||
}
|
||||
|
||||
public function testGetSetFFProbeDriver()
|
||||
{
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
$driver = $this->getFFProbeDriverMock();
|
||||
|
||||
$ffprobe->setFFProbeDriver($driver);
|
||||
$this->assertSame($driver, $ffprobe->getFFProbeDriver());
|
||||
}
|
||||
|
||||
public function testGetSetFFProbeMapper()
|
||||
{
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
$mapper = $this->getFFProbeMapperMock();
|
||||
|
||||
$ffprobe->setMapper($mapper);
|
||||
$this->assertSame($mapper, $ffprobe->getMapper());
|
||||
}
|
||||
|
||||
public function testGetSetOptionsTester()
|
||||
{
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
$tester = $this->getFFProbeOptionsTesterMock();
|
||||
|
||||
$ffprobe->setOptionsTester($tester);
|
||||
$this->assertSame($tester, $ffprobe->getOptionsTester());
|
||||
}
|
||||
|
||||
public function testGetSetCache()
|
||||
{
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
$cache = $this->getCacheMock();
|
||||
|
||||
$ffprobe->setCache($cache);
|
||||
$this->assertSame($cache, $ffprobe->getCache());
|
||||
}
|
||||
|
||||
public function provideDataWhitoutCache()
|
||||
{
|
||||
$stream = $this->getStreamMock();
|
||||
$format = $this->getFormatMock();
|
||||
|
||||
return [
|
||||
[$stream, 'streams', ['-show_streams', '-print_format'], FFProbe::TYPE_STREAMS, [__FILE__, '-show_streams', '-print_format', 'json'], false],
|
||||
[$format, 'format', ['-show_format', '-print_format'], FFProbe::TYPE_FORMAT, [__FILE__, '-show_format', '-print_format', 'json'], false],
|
||||
[$stream, 'streams', ['-show_streams'], FFProbe::TYPE_STREAMS, [__FILE__, '-show_streams'], true],
|
||||
[$format, 'format', ['-show_format'], FFProbe::TYPE_FORMAT, [__FILE__, '-show_format'], true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDataWhitoutCache
|
||||
*/
|
||||
public function testProbeWithoutCache($output, $method, $commands, $type, $caughtCommands, $isRaw)
|
||||
{
|
||||
$pathfile = __FILE__;
|
||||
$data = ['key' => 'value'];
|
||||
$rawData = 'raw data';
|
||||
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
|
||||
$mapper = $this->getFFProbeMapperMock();
|
||||
$mapper->expects($this->once())
|
||||
->method('map')
|
||||
->with($type, $data)
|
||||
->will($this->returnValue($output));
|
||||
|
||||
$parser = $this->getFFProbeParserMock();
|
||||
|
||||
if ($isRaw) {
|
||||
$parser->expects($this->once())
|
||||
->method('parse')
|
||||
->with($type, $rawData)
|
||||
->will($this->returnValue($data));
|
||||
} else {
|
||||
$parser->expects($this->never())
|
||||
->method('parse');
|
||||
}
|
||||
|
||||
$tester = $this->getFFProbeOptionsTesterMockWithOptions($commands);
|
||||
|
||||
$cache = $this->getCacheMock();
|
||||
$cache->expects($this->once())
|
||||
->method('hasItem')
|
||||
->will($this->returnValue(false));
|
||||
$cache->expects($this->once())
|
||||
->method('getItem')
|
||||
->will($this->returnValue(new CacheItem));
|
||||
$cache->expects($this->once())
|
||||
->method('save')
|
||||
->with($this->anything());
|
||||
|
||||
$driver = $this->getFFProbeDriverMock();
|
||||
$driver->expects($this->once())
|
||||
->method('command')
|
||||
->with($caughtCommands)
|
||||
->will($this->returnValue($isRaw ? $rawData : json_encode($data)));
|
||||
|
||||
$ffprobe->setOptionsTester($tester)
|
||||
->setCache($cache)
|
||||
->setMapper($mapper)
|
||||
->setFFProbeDriver($driver)
|
||||
->setParser($parser);
|
||||
|
||||
$this->assertEquals($output, call_user_func([$ffprobe, $method], $pathfile));
|
||||
}
|
||||
|
||||
public function provideDataForInvalidJson()
|
||||
{
|
||||
$stream = $this->getStreamMock();
|
||||
$format = $this->getFormatMock();
|
||||
|
||||
return [
|
||||
[$stream, 'streams', ['-show_streams', '-print_format'], FFProbe::TYPE_STREAMS, [__FILE__, '-show_streams', '-print_format', 'json']],
|
||||
[$format, 'format', ['-show_format', '-print_format'], FFProbe::TYPE_FORMAT, [__FILE__, '-show_format', '-print_format', 'json']],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDataForInvalidJson
|
||||
*/
|
||||
public function testProbeWithWrongJson($output, $method, $commands, $type, $caughtCommands)
|
||||
{
|
||||
$pathfile = __FILE__;
|
||||
$data = ['key' => 'value'];
|
||||
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
|
||||
$mapper = $this->getFFProbeMapperMock();
|
||||
$mapper->expects($this->once())
|
||||
->method('map')
|
||||
->with($this->isType('string'), 'good data parsed')
|
||||
->will($this->returnValue($output));
|
||||
|
||||
$parser = $this->getFFProbeParserMock();
|
||||
$parser->expects($this->once())
|
||||
->method('parse')
|
||||
->with($this->isType('string', json_encode($data).'lala'))
|
||||
->will($this->returnValue('good data parsed'));
|
||||
|
||||
$tester = $this->getFFProbeOptionsTesterMockWithOptions($commands);
|
||||
|
||||
$cache = $this->getCacheMock();
|
||||
$cache->expects($this->exactly(2))
|
||||
->method('hasItem')
|
||||
->will($this->returnValue(false));
|
||||
$cache->expects($this->once())
|
||||
->method('getItem')
|
||||
->will($this->returnValue(new CacheItem));
|
||||
|
||||
$driver = $this->getFFProbeDriverMock();
|
||||
$driver->expects($this->exactly(2))
|
||||
->method('command')
|
||||
->will($this->returnValue(json_encode($data).'lala'));
|
||||
|
||||
$ffprobe->setOptionsTester($tester)
|
||||
->setCache($cache)
|
||||
->setMapper($mapper)
|
||||
->setFFProbeDriver($driver)
|
||||
->setParser($parser);
|
||||
|
||||
$this->assertEquals($output, call_user_func([$ffprobe, $method], $pathfile));
|
||||
}
|
||||
|
||||
public function provideProbingDataWithCache()
|
||||
{
|
||||
$stream = $this->getStreamMock();
|
||||
$format = $this->getFormatMock();
|
||||
|
||||
return [
|
||||
[$stream, 'streams'],
|
||||
[$format, 'format'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideProbingDataWithCache
|
||||
*/
|
||||
public function testProbeWithCache($output, $method)
|
||||
{
|
||||
$pathfile = __FILE__;
|
||||
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
|
||||
$mapper = $this->getFFProbeMapperMock();
|
||||
$mapper->expects($this->never())
|
||||
->method('map');
|
||||
|
||||
$tester = $this->getFFProbeOptionsTesterMock();
|
||||
|
||||
$cacheItem = new CacheItem;
|
||||
$cacheItem->set($output);
|
||||
|
||||
$cache = $this->getCacheMock();
|
||||
$cache->expects($this->once())
|
||||
->method('hasItem')
|
||||
->will($this->returnValue(true));
|
||||
$cache->expects($this->once())
|
||||
->method('getItem')
|
||||
->will($this->returnValue($cacheItem));
|
||||
$cache->expects($this->never())
|
||||
->method('save');
|
||||
|
||||
$driver = $this->getFFProbeDriverMock();
|
||||
$driver->expects($this->never())
|
||||
->method('command');
|
||||
|
||||
$ffprobe->setOptionsTester($tester)
|
||||
->setCache($cache)
|
||||
->setMapper($mapper)
|
||||
->setFFProbeDriver($driver);
|
||||
|
||||
$this->assertEquals($output, call_user_func([$ffprobe, $method], $pathfile));
|
||||
}
|
||||
|
||||
public function provideProbeMethod()
|
||||
{
|
||||
return [
|
||||
['streams'],
|
||||
['format'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideProbeMethod
|
||||
*/
|
||||
public function testProbeWithoutShowStreamsAvailable($method)
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
$pathfile = __FILE__;
|
||||
|
||||
$ffprobe = new FFProbe($this->getFFProbeDriverMock(), $this->getCacheMock());
|
||||
$ffprobe->setOptionsTester($this->getFFProbeOptionsTesterMock());
|
||||
call_user_func([$ffprobe, $method], $pathfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCreateOptions
|
||||
*/
|
||||
public function testCreate($logger, $conf, $cache)
|
||||
{
|
||||
$finder = new ExecutableFinder();
|
||||
|
||||
$found = false;
|
||||
foreach (['avprobe', 'ffprobe'] as $name) {
|
||||
if (null !== $finder->find($name)) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$this->markTestSkipped('Unable to find avprobe or ffprobe on system');
|
||||
}
|
||||
|
||||
$ffprobe = FFProbe::create();
|
||||
$this->assertInstanceOf('FFMpeg\FFprobe', $ffprobe);
|
||||
|
||||
$ffprobe = FFProbe::create($conf, $logger, $cache);
|
||||
$this->assertInstanceOf('FFMpeg\FFprobe', $ffprobe);
|
||||
|
||||
if (null !== $cache) {
|
||||
$this->assertSame($cache, $ffprobe->getCache());
|
||||
}
|
||||
if (null !== $logger) {
|
||||
$this->assertSame($logger, $ffprobe->getFFProbeDriver()->getProcessRunner()->getLogger());
|
||||
}
|
||||
if ($conf instanceof ConfigurationInterface) {
|
||||
$this->assertSame($conf, $ffprobe->getFFProbeDriver()->getConfiguration());
|
||||
}
|
||||
}
|
||||
|
||||
public function provideCreateOptions()
|
||||
{
|
||||
return [
|
||||
[null, ['key' => 'value'], null],
|
||||
[$this->getLoggerMock(), ['key' => 'value'], null],
|
||||
[null, new Configuration(), null],
|
||||
[null, ['key' => 'value'], $this->getCacheMock()],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Audio;
|
||||
|
||||
use FFMpeg\Coordinate\TimeCode;
|
||||
use FFMpeg\Filters\Audio\AudioFilters;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AudioClipTest extends TestCase
|
||||
{
|
||||
public function testClipping()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$audio = $this->getAudioMock();
|
||||
$audio->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AudioClipFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filters = new AudioFilters($audio);
|
||||
|
||||
$filters->clip(TimeCode::fromSeconds(5));
|
||||
$this->assertEquals([0 => '-ss', 1 => '00:00:05.00', 2 => '-acodec', 3 => 'copy'], $capturedFilter->apply($audio, $format));
|
||||
}
|
||||
|
||||
public function testClippingWithDuration()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$audio = $this->getAudioMock();
|
||||
$audio->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AudioClipFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filters = new AudioFilters($audio);
|
||||
|
||||
$filters->clip(TimeCode::fromSeconds(5), TimeCode::fromSeconds(5));
|
||||
$this->assertEquals([0 => '-ss', 1 => '00:00:05.00', 2 => '-t', 3 => '00:00:05.00', 4 => '-acodec', 5 => 'copy'], $capturedFilter->apply($audio, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Audio;
|
||||
|
||||
use FFMpeg\Filters\Audio\AudioFilters;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AudioFiltersTest extends TestCase
|
||||
{
|
||||
public function testResample()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$audio = $this->getAudioMock();
|
||||
$audio->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AudioResamplableFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
|
||||
$filters = new AudioFilters($audio);
|
||||
$filters->resample(8000);
|
||||
$this->assertEquals(8000, $capturedFilter->getRate());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Audio;
|
||||
|
||||
use FFMpeg\Filters\Audio\AudioFilters;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AudioMetadataTest extends TestCase
|
||||
{
|
||||
public function testAddMetadata()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$audio = $this->getAudioMock();
|
||||
$audio->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AddMetadataFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filters = new AudioFilters($audio);
|
||||
$filters->addMetadata(['title' => 'Hello World']);
|
||||
$this->assertEquals([0 => '-metadata', 1 => 'title=Hello World'], $capturedFilter->apply($audio, $format));
|
||||
}
|
||||
|
||||
public function testAddArtwork()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$audio = $this->getAudioMock();
|
||||
$audio->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AddMetadataFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filters = new AudioFilters($audio);
|
||||
$filters->addMetadata(['genre' => 'Some Genre', 'artwork' => '/path/to/file.jpg']);
|
||||
$this->assertEquals([0 => '-i', 1 => '/path/to/file.jpg', 2 => '-map', 3 => '0', 4 => '-map', 5 => '1', 6 => '-metadata', 7 => 'genre=Some Genre'], $capturedFilter->apply($audio, $format));
|
||||
$this->assertEquals([0 => '-i', 1 => '/path/to/file.jpg', 2 => '-map', 3 => '0', 4 => '-map', 5 => '1', 6 => '-metadata', 7 => 'genre=Some Genre'], $capturedFilter->apply($audio, $format));
|
||||
}
|
||||
|
||||
public function testRemoveMetadata()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$audio = $this->getAudioMock();
|
||||
$audio->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Audio\AddMetadataFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filters = new AudioFilters($audio);
|
||||
$filters->addMetadata();
|
||||
$this->assertEquals([0 => '-map_metadata', 1 => '-1', 2 => '-vn'], $capturedFilter->apply($audio, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Audio;
|
||||
|
||||
use FFMpeg\Filters\Audio\AudioResamplableFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AudioResamplableFilterTest extends TestCase
|
||||
{
|
||||
public function testGetRate()
|
||||
{
|
||||
$filter = new AudioResamplableFilter(500);
|
||||
$this->assertEquals(500, $filter->getRate());
|
||||
}
|
||||
|
||||
public function testApply()
|
||||
{
|
||||
$audio = $this->getAudioMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filter = new AudioResamplableFilter(500);
|
||||
$this->assertEquals(['-ac', 2, '-ar', 500], $filter->apply($audio, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Audio;
|
||||
|
||||
use FFMpeg\Filters\Audio\CustomFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class CustomFilterTest extends TestCase
|
||||
{
|
||||
public function testApplyCustomFilter()
|
||||
{
|
||||
$audio = $this->getAudioMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
$filter = new CustomFilter('whatever i put would end up as a filter');
|
||||
$this->assertEquals(['-af', 'whatever i put would end up as a filter'], $filter->apply($audio, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters;
|
||||
|
||||
use FFMpeg\Filters\Audio\SimpleFilter;
|
||||
use FFMpeg\Filters\FiltersCollection;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class FiltersCollectionTest extends TestCase
|
||||
{
|
||||
public function testCount()
|
||||
{
|
||||
$coll = new FiltersCollection();
|
||||
$this->assertCount(0, $coll);
|
||||
|
||||
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
|
||||
$this->assertCount(1, $coll);
|
||||
|
||||
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
|
||||
$this->assertCount(2, $coll);
|
||||
}
|
||||
|
||||
public function testIterator()
|
||||
{
|
||||
$coll = new FiltersCollection();
|
||||
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
|
||||
$coll->add($this->getMockBuilder('FFMpeg\Filters\FilterInterface')->getMock());
|
||||
|
||||
$this->assertInstanceOf('\ArrayIterator', $coll->getIterator());
|
||||
$this->assertCount(2, $coll->getIterator());
|
||||
}
|
||||
|
||||
public function testEmptyIterator()
|
||||
{
|
||||
$coll = new FiltersCollection();
|
||||
$this->assertInstanceOf('\ArrayIterator', $coll->getIterator());
|
||||
}
|
||||
|
||||
public function testIteratorSort()
|
||||
{
|
||||
$coll = new FiltersCollection();
|
||||
$coll->add(new SimpleFilter(['a']));
|
||||
$coll->add(new SimpleFilter(['1'], 12));
|
||||
$coll->add(new SimpleFilter(['b']));
|
||||
$coll->add(new SimpleFilter(['2'], 12));
|
||||
$coll->add(new SimpleFilter(['c']));
|
||||
$coll->add(new SimpleFilter(['3'], 10));
|
||||
$coll->add(new SimpleFilter(['d']));
|
||||
$coll->add(new SimpleFilter(['4'], -2));
|
||||
$coll->add(new SimpleFilter(['e']));
|
||||
|
||||
$data = [];
|
||||
$video = $this->getVideoMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
|
||||
foreach ($coll as $filter) {
|
||||
$data = array_merge($data, $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
$this->assertEquals(['1', '2', '3', 'a', 'b', 'c', 'd', 'e', '4'], $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Frame;
|
||||
|
||||
use FFMpeg\Filters\Frame\CustomFrameFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class CustomFrameFilterTest extends TestCase
|
||||
{
|
||||
public function testApplyCustomFrameFilter()
|
||||
{
|
||||
$frame = $this->getFrameMock();
|
||||
|
||||
$filter = new CustomFrameFilter('whatever i put would end up as a filter');
|
||||
$this->assertEquals(['-vf', 'whatever i put would end up as a filter'], $filter->apply($frame));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Frame;
|
||||
|
||||
use FFMpeg\Coordinate\TimeCode;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Frame\DisplayRatioFixerFilter;
|
||||
use FFMpeg\Media\Frame;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class DisplayRatioFixerFilterTest extends TestCase
|
||||
{
|
||||
public function testApply()
|
||||
{
|
||||
$stream = new Stream(['codec_type' => 'video', 'width' => 960, 'height' => 720]);
|
||||
$streams = new StreamCollection([$stream]);
|
||||
|
||||
$video = $this->getVideoMock(__FILE__);
|
||||
$video->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$frame = new Frame($video, $this->getFFMpegDriverMock(), $this->getFFProbeMock(), new TimeCode(0, 0, 0, 0));
|
||||
$filter = new DisplayRatioFixerFilter();
|
||||
$this->assertEquals(['-s', '960x720'], $filter->apply($frame));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Frame;
|
||||
|
||||
use FFMpeg\Filters\Frame\FrameFilters;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class FrameFiltersTest extends TestCase
|
||||
{
|
||||
public function testResize()
|
||||
{
|
||||
$frame = $this->getFrameMock();
|
||||
$filters = new FrameFilters($frame);
|
||||
|
||||
$frame->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Frame\DisplayRatioFixerFilter'));
|
||||
|
||||
$filters->fixDisplayRatio();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\Coordinate\Point;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\CropFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class CropFilterTest extends TestCase
|
||||
{
|
||||
public function testCommandParamsAreCorrectAndStreamIsUpdated()
|
||||
{
|
||||
$stream = new Stream(['width' => 320, 'height' => 240, 'codec_type' => 'video']);
|
||||
$streams = new StreamCollection([$stream]);
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$video->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$dimension = new Dimension(200, 150);
|
||||
$point = new Point(25, 35);
|
||||
$filter = new CropFilter($point, $dimension);
|
||||
$expected = [
|
||||
'-filter:v',
|
||||
'crop='.$dimension->getWidth().':'.$dimension->getHeight().':'.$point->getX().':'.$point->getY(),
|
||||
];
|
||||
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||
|
||||
$this->assertEquals(200, $stream->get('width'));
|
||||
$this->assertEquals(150, $stream->get('height'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Filters\Video\CustomFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class CustomFilterTest extends TestCase
|
||||
{
|
||||
public function testApplyCustomFilter()
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$filter = new CustomFilter('whatever i put would end up as a filter');
|
||||
$this->assertEquals(['-vf', 'whatever i put would end up as a filter'], $filter->apply($video, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\ExtractMultipleFramesFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class ExtractMultipleFramesFilterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideFrameRates
|
||||
*/
|
||||
public function testApply($frameRate, $frameFileType, $destinationFolder, $duration, $modulus, $expected)
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$pathfile = '/path/to/file'.mt_rand();
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getModulus')
|
||||
->will($this->returnValue($modulus));
|
||||
|
||||
$streams = new StreamCollection([
|
||||
new Stream([
|
||||
'codec_type' => 'video',
|
||||
'duration' => $duration,
|
||||
]),
|
||||
]);
|
||||
|
||||
$video->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$filter = new ExtractMultipleFramesFilter($frameRate, $destinationFolder);
|
||||
$filter->setFrameFileType($frameFileType);
|
||||
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
public function provideFrameRates()
|
||||
{
|
||||
return [
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/1', '/frame-%03d.jpg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/2', '/frame-%02d.jpg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/5', '/frame-%02d.jpg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/10', '/frame-%02d.jpg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/30', '/frame-%02d.jpg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'jpg', '/', 100, 2, ['-vf', 'fps=1/60', '/frame-%02d.jpg']],
|
||||
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/1', '/frame-%03d.jpeg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/2', '/frame-%02d.jpeg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/5', '/frame-%02d.jpeg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/10', '/frame-%02d.jpeg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/30', '/frame-%02d.jpeg']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'jpeg', '/', 100, 2, ['-vf', 'fps=1/60', '/frame-%02d.jpeg']],
|
||||
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/1', '/frame-%03d.png']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_2SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/2', '/frame-%02d.png']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_5SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/5', '/frame-%02d.png']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_10SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/10', '/frame-%02d.png']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_30SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/30', '/frame-%02d.png']],
|
||||
[ExtractMultipleFramesFilter::FRAMERATE_EVERY_60SEC, 'png', '/', 100, 2, ['-vf', 'fps=1/60', '/frame-%02d.png']],
|
||||
];
|
||||
}
|
||||
|
||||
public function testInvalidFrameFileType()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$filter = new ExtractMultipleFramesFilter('1/1', '/');
|
||||
$filter->setFrameFileType('webm');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Coordinate\FrameRate;
|
||||
use FFMpeg\Filters\Video\FrameRateFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class FrameRateFilterTest extends TestCase
|
||||
{
|
||||
public function testApplyWithAFormatThatSupportsBFrames()
|
||||
{
|
||||
$framerate = new FrameRate(54);
|
||||
$gop = 42;
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('supportBFrames')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$expected = ['-r', 54, '-b_strategy', '1', '-bf', '3', '-g', 42];
|
||||
|
||||
$filter = new FrameRateFilter($framerate, $gop);
|
||||
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
public function testApplyWithAFormatThatDoesNotSupportsBFrames()
|
||||
{
|
||||
$framerate = new FrameRate(54);
|
||||
$gop = 42;
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('supportBFrames')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$expected = ['-r', 54];
|
||||
|
||||
$filter = new FrameRateFilter($framerate, $gop);
|
||||
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\PadFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class PadFilterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideDimensions
|
||||
*/
|
||||
public function testApply(Dimension $dimension, $width, $height, $expected)
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$pathfile = '/path/to/file'.mt_rand();
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$streams = new StreamCollection([
|
||||
new Stream([
|
||||
'codec_type' => 'video',
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
]),
|
||||
]);
|
||||
|
||||
$filter = new PadFilter($dimension);
|
||||
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
public function provideDimensions()
|
||||
{
|
||||
return [
|
||||
[new Dimension(1000, 800), 640, 480, ['-vf', 'scale=iw*min(1000/iw\,800/ih):ih*min(1000/iw\,800/ih),pad=1000:800:(1000-iw)/2:(800-ih)/2']],
|
||||
[new Dimension(300, 600), 640, 480, ['-vf', 'scale=iw*min(300/iw\,600/ih):ih*min(300/iw\,600/ih),pad=300:600:(300-iw)/2:(600-ih)/2']],
|
||||
[new Dimension(100, 900), 640, 480, ['-vf', 'scale=iw*min(100/iw\,900/ih):ih*min(100/iw\,900/ih),pad=100:900:(100-iw)/2:(900-ih)/2']],
|
||||
[new Dimension(1200, 200), 640, 480, ['-vf', 'scale=iw*min(1200/iw\,200/ih):ih*min(1200/iw\,200/ih),pad=1200:200:(1200-iw)/2:(200-ih)/2']],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Coordinate\Dimension;
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\ResizeFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class ResizeFilterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideDimensions
|
||||
*/
|
||||
public function testApply(Dimension $dimension, $mode, $width, $height, $modulus, $expected, $forceStandards = true)
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$pathfile = '/path/to/file'.mt_rand();
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getModulus')
|
||||
->will($this->returnValue($modulus));
|
||||
|
||||
$streams = new StreamCollection([
|
||||
new Stream([
|
||||
'codec_type' => 'video',
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
]),
|
||||
]);
|
||||
|
||||
$video->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$filter = new ResizeFilter($dimension, $mode, $forceStandards);
|
||||
$this->assertEquals($expected, $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
public function provideDimensions()
|
||||
{
|
||||
return [
|
||||
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_FIT, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
|
||||
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_INSET, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
|
||||
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
|
||||
[new Dimension(320, 240), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
|
||||
|
||||
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_FIT, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
|
||||
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_INSET, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
|
||||
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
|
||||
[new Dimension(640, 480), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 320, 240, 2, ['-vf', '[in]scale=640:480 [out]']],
|
||||
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_FIT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_INSET, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_FIT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_INSET, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
[new Dimension(640, 360), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 1280, 720, 2, ['-vf', '[in]scale=640:360 [out]']],
|
||||
|
||||
// test non standard dimension
|
||||
[new Dimension(700, 150), ResizeFilter::RESIZEMODE_INSET, 123, 456, 2, ['-vf', '[in]scale=62:150 [out]'], true],
|
||||
[new Dimension(700, 150), ResizeFilter::RESIZEMODE_INSET, 123, 456, 2, ['-vf', '[in]scale=40:150 [out]'], false],
|
||||
|
||||
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_FIT, 640, 480, 2, ['-vf', '[in]scale=320:320 [out]']],
|
||||
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_INSET, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
|
||||
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_SCALE_HEIGHT, 640, 480, 2, ['-vf', '[in]scale=320:240 [out]']],
|
||||
[new Dimension(320, 320), ResizeFilter::RESIZEMODE_SCALE_WIDTH, 640, 480, 2, ['-vf', '[in]scale=426:320 [out]']],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\RotateFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class RotateFilterTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provide90degresTranspositions
|
||||
*/
|
||||
public function testApplyWithSizeTransformation($value)
|
||||
{
|
||||
$stream = new Stream(['width' => 320, 'height' => 240, 'codec_type' => 'video']);
|
||||
$streams = new StreamCollection([$stream]);
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$video->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$filter = new RotateFilter($value);
|
||||
$this->assertEquals(['-vf', $value, '-metadata:s:v:0', 'rotate=0'], $filter->apply($video, $format));
|
||||
|
||||
$this->assertEquals(240, $stream->get('width'));
|
||||
$this->assertEquals(320, $stream->get('height'));
|
||||
}
|
||||
|
||||
public function provide90degresTranspositions()
|
||||
{
|
||||
return [
|
||||
[RotateFilter::ROTATE_90],
|
||||
[RotateFilter::ROTATE_270],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDegresWithoutTranspositions
|
||||
*/
|
||||
public function testApplyWithoutSizeTransformation($value)
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$video->expects($this->never())
|
||||
->method('getStreams');
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$filter = new RotateFilter($value);
|
||||
$this->assertEquals(['-vf', $value, '-metadata:s:v:0', 'rotate=0'], $filter->apply($video, $format));
|
||||
}
|
||||
|
||||
public function provideDegresWithoutTranspositions()
|
||||
{
|
||||
return [
|
||||
[RotateFilter::ROTATE_180],
|
||||
];
|
||||
}
|
||||
|
||||
public function testApplyInvalidAngle()
|
||||
{
|
||||
$this->expectException(
|
||||
'\FFMpeg\Exception\InvalidArgumentException',
|
||||
'Invalid angle value.'
|
||||
);
|
||||
new RotateFilter('90');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Filters\Video\SynchronizeFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class SynchronizeFilterTest extends TestCase
|
||||
{
|
||||
public function testApply()
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$filter = new SynchronizeFilter();
|
||||
$this->assertEquals(['-async', '1', '-metadata:s:v:0', 'start_time=0'], $filter->apply($video, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\Filters\Video\ResizeFilter;
|
||||
use FFMpeg\Filters\Video\VideoFilters;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class VideoFiltersTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideResizeOptions
|
||||
*/
|
||||
public function testResize($mode, $forceStandards)
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$filters = new VideoFilters($video);
|
||||
$dimension = $this->getDimensionMock();
|
||||
|
||||
$video->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Video\ResizeFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
|
||||
$filters->resize($dimension, $mode, $forceStandards);
|
||||
|
||||
$this->assertSame($mode, $capturedFilter->getMode());
|
||||
$this->assertSame($forceStandards, $capturedFilter->areStandardsForced());
|
||||
$this->assertSame($dimension, $capturedFilter->getDimension());
|
||||
}
|
||||
|
||||
public function provideResizeOptions()
|
||||
{
|
||||
return [
|
||||
[ResizeFilter::RESIZEMODE_FIT, true],
|
||||
[ResizeFilter::RESIZEMODE_SCALE_WIDTH, true],
|
||||
[ResizeFilter::RESIZEMODE_SCALE_HEIGHT, false],
|
||||
[ResizeFilter::RESIZEMODE_INSET, false],
|
||||
];
|
||||
}
|
||||
|
||||
public function testResample()
|
||||
{
|
||||
$capturedFilter = null;
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
$filters = new VideoFilters($video);
|
||||
$framerate = $this->getFramerateMock();
|
||||
$gop = 42;
|
||||
|
||||
$video->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Video\FrameRateFilter'))
|
||||
->will($this->returnCallback(function ($filter) use (&$capturedFilter) {
|
||||
$capturedFilter = $filter;
|
||||
}));
|
||||
|
||||
$filters->framerate($framerate, $gop);
|
||||
|
||||
$this->assertSame($framerate, $capturedFilter->getFramerate());
|
||||
$this->assertSame($gop, $capturedFilter->getGOP());
|
||||
}
|
||||
|
||||
public function testSynchronize()
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$filters = new VideoFilters($video);
|
||||
|
||||
$video->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Video\SynchronizeFilter'));
|
||||
|
||||
$filters->synchronize();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Video;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Video\WatermarkFilter;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class WatermarkFilterTest extends TestCase
|
||||
{
|
||||
public function testApplyWatermark()
|
||||
{
|
||||
$stream = new Stream(['width' => 320, 'height' => 240, 'codec_type' => 'video']);
|
||||
$streams = new StreamCollection([$stream]);
|
||||
|
||||
$video = $this->getVideoMock();
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png');
|
||||
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=0:0 [out]'], $filter->apply($video, $format));
|
||||
|
||||
// check size of video is unchanged
|
||||
$this->assertEquals(320, $stream->get('width'));
|
||||
$this->assertEquals(240, $stream->get('height'));
|
||||
}
|
||||
|
||||
public function testDifferentCoordinaates()
|
||||
{
|
||||
$video = $this->getVideoMock();
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
|
||||
// test position absolute
|
||||
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
|
||||
'position' => 'absolute',
|
||||
'x' => 10, 'y' => 5,
|
||||
]);
|
||||
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=10:5 [out]'], $filter->apply($video, $format));
|
||||
|
||||
// test position relative
|
||||
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
|
||||
'position' => 'relative',
|
||||
'bottom' => 10, 'left' => 5,
|
||||
]);
|
||||
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=5:main_h - 10 - overlay_h [out]'], $filter->apply($video, $format));
|
||||
|
||||
// test position relative
|
||||
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
|
||||
'position' => 'relative',
|
||||
'bottom' => 5, 'right' => 4,
|
||||
]);
|
||||
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=main_w - 4 - overlay_w:main_h - 5 - overlay_h [out]'], $filter->apply($video, $format));
|
||||
|
||||
// test position relative
|
||||
$filter = new WatermarkFilter(__DIR__.'/../../../files/watermark.png', [
|
||||
'position' => 'relative',
|
||||
'left' => 5, 'top' => 11,
|
||||
]);
|
||||
$this->assertEquals(['-vf', 'movie='.__DIR__.'/../../../files/watermark.png [watermark]; [in][watermark] overlay=5:11 [out]'], $filter->apply($video, $format));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Waveform;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Stream;
|
||||
use FFMpeg\FFProbe\DataMapping\StreamCollection;
|
||||
use FFMpeg\Filters\Waveform\WaveformDownmixFilter;
|
||||
use FFMpeg\Media\Waveform;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class WaveformDownmixFilterTest extends TestCase
|
||||
{
|
||||
public function testApply()
|
||||
{
|
||||
$stream = new Stream(['codec_type' => 'audio', 'width' => 960, 'height' => 720]);
|
||||
$streams = new StreamCollection([$stream]);
|
||||
|
||||
$audio = $this->getAudioMock(__FILE__);
|
||||
$audio->expects($this->once())
|
||||
->method('getStreams')
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$waveform = new Waveform($audio, $this->getFFMpegDriverMock(), $this->getFFProbeMock(), 640, 120);
|
||||
$filter = new WaveformDownmixFilter(true);
|
||||
$this->assertEquals(['"aformat=channel_layouts=mono"'], $filter->apply($waveform));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Filters\Waveform;
|
||||
|
||||
use FFMpeg\Filters\Waveform\WaveformFilters;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class WaveformFiltersTest extends TestCase
|
||||
{
|
||||
public function testResize()
|
||||
{
|
||||
$Waveform = $this->getWaveformMock();
|
||||
$filters = new WaveformFilters($Waveform);
|
||||
|
||||
$Waveform->expects($this->once())
|
||||
->method('addFilter')
|
||||
->with($this->isInstanceOf('FFMpeg\Filters\Waveform\WaveformDownmixFilter'));
|
||||
|
||||
$filters->setDownmix();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Audio;
|
||||
|
||||
use FFMpeg\Format\Audio\Aac;
|
||||
|
||||
class AacTest extends AudioTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new Aac();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Audio;
|
||||
|
||||
use FFMpeg\Format\Audio\DefaultAudio;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
abstract class AudioTestCase extends TestCase
|
||||
{
|
||||
public function testExtraParams()
|
||||
{
|
||||
$extraParams = $this->getFormat()->getExtraParams();
|
||||
|
||||
$this->assertIsArray($extraParams);
|
||||
|
||||
foreach ($extraParams as $param) {
|
||||
$this->assertScalar($param);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetAudioCodec()
|
||||
{
|
||||
$this->assertScalar($this->getFormat()->getAudioCodec());
|
||||
$this->assertContains($this->getFormat()->getAudioCodec(), $this->getFormat()->getAvailableAudioCodecs());
|
||||
}
|
||||
|
||||
public function testSetAudioCodec()
|
||||
{
|
||||
$format = $this->getFormat();
|
||||
|
||||
foreach ($format->getAvailableAudioCodecs() as $codec) {
|
||||
$format->setAudioCodec($codec);
|
||||
$this->assertEquals($codec, $format->getAudioCodec());
|
||||
}
|
||||
}
|
||||
|
||||
public function testSetInvalidAudioCodec()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$this->getFormat()->setAudioCodec('invalid-random-audio-codec');
|
||||
}
|
||||
|
||||
public function testGetAvailableAudioCodecs()
|
||||
{
|
||||
$this->assertGreaterThan(0, count($this->getFormat()->getAvailableAudioCodecs()));
|
||||
}
|
||||
|
||||
public function testGetAudioKiloBitrate()
|
||||
{
|
||||
$this->assertIsInt($this->getFormat()->getAudioKiloBitrate());
|
||||
}
|
||||
|
||||
public function testSetAudioKiloBitrate()
|
||||
{
|
||||
$format = $this->getFormat();
|
||||
$format->setAudioKiloBitrate(256);
|
||||
$this->assertEquals(256, $format->getAudioKiloBitrate());
|
||||
}
|
||||
|
||||
public function testSetInvalidKiloBitrate()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$this->getFormat()->setAudioKiloBitrate(0);
|
||||
}
|
||||
|
||||
public function testSetNegativeKiloBitrate()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$this->getFormat()->setAudioKiloBitrate(-10);
|
||||
}
|
||||
|
||||
public function testGetAudioChannels()
|
||||
{
|
||||
$this->assertNull($this->getFormat()->getAudioChannels());
|
||||
}
|
||||
|
||||
public function testSetAudioChannels()
|
||||
{
|
||||
$format = $this->getFormat();
|
||||
$format->setAudioChannels(2);
|
||||
$this->assertEquals(2, $format->getAudioChannels());
|
||||
}
|
||||
|
||||
public function testSetInvalidChannels()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$this->getFormat()->setAudioChannels(0);
|
||||
}
|
||||
|
||||
public function testSetNegativeChannels()
|
||||
{
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$this->getFormat()->setAudioChannels(-10);
|
||||
}
|
||||
|
||||
public function testCreateProgressListener()
|
||||
{
|
||||
$media = $this->getMockBuilder('FFMpeg\Media\MediaTypeInterface')->getMock();
|
||||
$media->expects($this->any())
|
||||
->method('getPathfile')
|
||||
->will($this->returnValue(__FILE__));
|
||||
$format = $this->getFormat();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
foreach ($format->createProgressListener($media, $ffprobe, 1, 3) as $listener) {
|
||||
$this->assertInstanceOf('FFMpeg\Format\ProgressListener\AudioProgressListener', $listener);
|
||||
$this->assertSame($ffprobe, $listener->getFFProbe());
|
||||
$this->assertSame(__FILE__, $listener->getPathfile());
|
||||
$this->assertSame(1, $listener->getCurrentPass());
|
||||
$this->assertSame(3, $listener->getTotalPass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DefaultAudio
|
||||
*/
|
||||
abstract public function getFormat();
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Audio;
|
||||
|
||||
use FFMpeg\Format\Audio\Flac;
|
||||
|
||||
class FlacTest extends AudioTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new Flac();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Audio;
|
||||
|
||||
use FFMpeg\Format\Audio\Mp3;
|
||||
|
||||
class Mp3Test extends AudioTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new Mp3();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Audio;
|
||||
|
||||
use FFMpeg\Format\Audio\Vorbis;
|
||||
|
||||
class VorbisTest extends AudioTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new Vorbis();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Audio;
|
||||
|
||||
use FFMpeg\Format\Audio\Wav;
|
||||
|
||||
class WavTest extends AudioTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new Wav();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\ProgressListener;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Format;
|
||||
use FFMpeg\Format\ProgressListener\AudioProgressListener;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class AudioProgressListenerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData
|
||||
*/
|
||||
public function testHandle(
|
||||
$size,
|
||||
$duration,
|
||||
$data,
|
||||
$expectedPercent,
|
||||
$expectedRemaining,
|
||||
$expectedRate,
|
||||
$data2,
|
||||
$expectedPercent2,
|
||||
$expectedRemaining2,
|
||||
$expectedRate2,
|
||||
$currentPass,
|
||||
$totalPass
|
||||
) {
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('format')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue(new Format([
|
||||
'size' => $size,
|
||||
'duration' => $duration,
|
||||
])));
|
||||
|
||||
$listener = new AudioProgressListener($ffprobe, __FILE__, $currentPass, $totalPass);
|
||||
$phpunit = $this;
|
||||
$n = 0;
|
||||
$listener->on('progress', function ($percent, $remaining, $rate) use (&$n, $phpunit, $expectedPercent, $expectedRemaining, $expectedRate, $expectedPercent2, $expectedRemaining2, $expectedRate2) {
|
||||
if (0 === $n) {
|
||||
$phpunit->assertEquals($expectedPercent, $percent);
|
||||
$phpunit->assertEquals($expectedRemaining, $remaining);
|
||||
$phpunit->assertEquals($expectedRate, $rate);
|
||||
} elseif (1 === $n) {
|
||||
$phpunit->assertEquals($expectedPercent2, $percent);
|
||||
$phpunit->assertEquals($expectedRemaining2, $remaining);
|
||||
$phpunit->assertLessThan($expectedRate2 + 3, $rate);
|
||||
$phpunit->assertGreaterThan($expectedRate2 - 3, $rate);
|
||||
}
|
||||
++$n;
|
||||
});
|
||||
// first one does not trigger progress event
|
||||
$listener->handle('any-type'.mt_rand(), $data);
|
||||
sleep(1);
|
||||
$listener->handle('any-type'.mt_rand(), $data);
|
||||
sleep(1);
|
||||
$listener->handle('any-type'.mt_rand(), $data2);
|
||||
$this->assertEquals(2, $n);
|
||||
}
|
||||
|
||||
public function provideData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
2894412,
|
||||
180.900750,
|
||||
'size= 712kB time=00:00:45.50 bitrate= 128.1kbits/s',
|
||||
25,
|
||||
0,
|
||||
0,
|
||||
'size= 1274kB time=00:01:29.32 bitrate= 142.8kbits/s',
|
||||
49,
|
||||
2,
|
||||
563,
|
||||
1,
|
||||
1,
|
||||
],
|
||||
[
|
||||
2894412,
|
||||
180.900750,
|
||||
'size= 712kB time=00:00:45.50 bitrate= 128.1kbits/s',
|
||||
12,
|
||||
0,
|
||||
0,
|
||||
'size= 1274kB time=00:01:29.32 bitrate= 142.8kbits/s',
|
||||
24,
|
||||
2,
|
||||
563,
|
||||
1,
|
||||
2,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\ProgressListener;
|
||||
|
||||
use FFMpeg\FFProbe\DataMapping\Format;
|
||||
use FFMpeg\Format\ProgressListener\VideoProgressListener;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class VideoProgressListenerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData
|
||||
*/
|
||||
public function testHandle(
|
||||
$size,
|
||||
$duration,
|
||||
$newVideoDuration,
|
||||
$data,
|
||||
$expectedPercent,
|
||||
$expectedRemaining,
|
||||
$expectedRate,
|
||||
$data2,
|
||||
$expectedPercent2,
|
||||
$expectedRemaining2,
|
||||
$expectedRate2,
|
||||
$currentPass,
|
||||
$totalPass
|
||||
) {
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$ffprobe->expects($this->once())
|
||||
->method('format')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue(new Format([
|
||||
'size' => $size,
|
||||
'duration' => $duration,
|
||||
])));
|
||||
|
||||
$listener = new VideoProgressListener($ffprobe, __FILE__, $currentPass, $totalPass, $newVideoDuration);
|
||||
$phpunit = $this;
|
||||
$n = 0;
|
||||
$listener->on('progress', function ($percent, $remaining, $rate) use (&$n, $phpunit, $expectedPercent, $expectedRemaining, $expectedRate, $expectedPercent2, $expectedRemaining2, $expectedRate2) {
|
||||
if (0 === $n) {
|
||||
$phpunit->assertEquals($expectedPercent, $percent);
|
||||
$phpunit->assertEquals($expectedRemaining, $remaining);
|
||||
$phpunit->assertEquals($expectedRate, $rate);
|
||||
} elseif (1 === $n) {
|
||||
$phpunit->assertEquals($expectedPercent2, $percent);
|
||||
$phpunit->assertEquals($expectedRemaining2, $remaining);
|
||||
$phpunit->assertLessThan($expectedRate2 + 10, $rate);
|
||||
$phpunit->assertGreaterThan($expectedRate2 - 10, $rate);
|
||||
}
|
||||
++$n;
|
||||
});
|
||||
// first one does not trigger progress event
|
||||
$listener->handle('any-type'.mt_rand(), $data);
|
||||
sleep(1);
|
||||
$listener->handle('any-type'.mt_rand(), $data);
|
||||
sleep(1);
|
||||
$listener->handle('any-type'.mt_rand(), $data2);
|
||||
$this->assertEquals(2, $n);
|
||||
}
|
||||
|
||||
public function provideData()
|
||||
{
|
||||
return [
|
||||
[
|
||||
147073958,
|
||||
281.147533,
|
||||
281.147533,
|
||||
'frame= 206 fps=202 q=10.0 size= 571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0',
|
||||
2,
|
||||
0,
|
||||
0,
|
||||
'frame= 854 fps=113 q=20.0 size= 4430kB time=00:00:33.04 bitrate=1098.5kbits/s dup=36 drop=0',
|
||||
11,
|
||||
32,
|
||||
3868,
|
||||
1,
|
||||
1,
|
||||
],
|
||||
[
|
||||
147073958,
|
||||
281.147533,
|
||||
281.147533,
|
||||
'frame= 206 fps=202 q=10.0 size= 571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0',
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
'frame= 854 fps=113 q=20.0 size= 4430kB time=00:00:33.04 bitrate=1098.5kbits/s dup=36 drop=0',
|
||||
5,
|
||||
32,
|
||||
3868,
|
||||
1,
|
||||
2,
|
||||
],
|
||||
[
|
||||
147073958,
|
||||
281.147533,
|
||||
35,
|
||||
'frame= 206 fps=202 q=10.0 size= 571kB time=00:00:07.12 bitrate= 656.8kbits/s dup=9 drop=0',
|
||||
60,
|
||||
0,
|
||||
0,
|
||||
'frame= 854 fps=113 q=20.0 size= 4430kB time=00:00:33.04 bitrate=1098.5kbits/s dup=36 drop=0',
|
||||
97,
|
||||
0,
|
||||
3868,
|
||||
2,
|
||||
2,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use FFMpeg\Format\Video\X264;
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
class InitialParametersTest extends TestCase
|
||||
{
|
||||
public function testApplyInitialParameters()
|
||||
{
|
||||
$format = new X264();
|
||||
$format->setInitialParameters(['-acodec', 'libopus']);
|
||||
$this->assertEquals(['-acodec', 'libopus'], $format->getInitialParameters());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use FFMpeg\Format\Video\Ogg;
|
||||
|
||||
class OggTest extends VideoTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new Ogg();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use Tests\FFMpeg\Unit\Format\Audio\AudioTestCase;
|
||||
|
||||
abstract class VideoTestCase extends AudioTestCase
|
||||
{
|
||||
public function testGetVideoCodec()
|
||||
{
|
||||
$this->assertScalar($this->getFormat()->getVideoCodec());
|
||||
$this->assertContains($this->getFormat()->getVideoCodec(), $this->getFormat()->getAvailableVideoCodecs());
|
||||
}
|
||||
|
||||
public function testSupportBFrames()
|
||||
{
|
||||
$this->assertIsBool($this->getFormat()->supportBFrames());
|
||||
}
|
||||
|
||||
public function testSetVideoCodec()
|
||||
{
|
||||
$format = $this->getFormat();
|
||||
|
||||
foreach ($format->getAvailableVideoCodecs() as $codec) {
|
||||
$format->setVideoCodec($codec);
|
||||
$this->assertEquals($codec, $format->getVideoCodec());
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetKiloBitrate()
|
||||
{
|
||||
$this->assertIsInt($this->getFormat()->getKiloBitrate());
|
||||
}
|
||||
|
||||
public function testSetKiloBitrate()
|
||||
{
|
||||
$format = $this->getFormat();
|
||||
$format->setKiloBitrate(2560);
|
||||
$this->assertEquals(2560, $format->getKiloBitrate());
|
||||
}
|
||||
|
||||
public function testSetKiloBitrateBelowZero()
|
||||
{
|
||||
$this->expectException('FFMpeg\Exception\InvalidArgumentException');
|
||||
$format = $this->getFormat();
|
||||
$format->setKiloBitrate(-1);
|
||||
}
|
||||
|
||||
public function testSetInvalidVideoCodec()
|
||||
{
|
||||
$this->expectException('FFMpeg\Exception\InvalidArgumentException');
|
||||
$this->getFormat()->setVideoCodec('invalid-random-video-codec');
|
||||
}
|
||||
|
||||
public function testGetAvailableVideoCodecs()
|
||||
{
|
||||
$this->assertGreaterThan(0, count($this->getFormat()->getAvailableVideoCodecs()));
|
||||
}
|
||||
|
||||
public function testCreateProgressListener()
|
||||
{
|
||||
$media = $this->getMockBuilder('FFMpeg\Media\MediaTypeInterface')->getMock();
|
||||
$media->expects($this->any())
|
||||
->method('getPathfile')
|
||||
->will($this->returnValue(__FILE__));
|
||||
$format = $this->getFormat();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
foreach ($format->createProgressListener($media, $ffprobe, 1, 3) as $listener) {
|
||||
$this->assertInstanceOf('FFMpeg\Format\ProgressListener\VideoProgressListener', $listener);
|
||||
$this->assertSame($ffprobe, $listener->getFFProbe());
|
||||
$this->assertSame(__FILE__, $listener->getPathfile());
|
||||
$this->assertSame(1, $listener->getCurrentPass());
|
||||
$this->assertSame(3, $listener->getTotalPass());
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetPasses()
|
||||
{
|
||||
$this->assertIsInt($this->getFormat()->getPasses());
|
||||
$this->assertGreaterThan(0, $this->getFormat()->getPasses());
|
||||
}
|
||||
|
||||
public function testGetModulus()
|
||||
{
|
||||
$this->assertIsInt($this->getFormat()->getModulus());
|
||||
$this->assertGreaterThan(0, $this->getFormat()->getModulus());
|
||||
$this->assertEquals(0, $this->getFormat()->getModulus() % 2);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use FFMpeg\Format\Video\WMV3;
|
||||
|
||||
class WMV3Test extends VideoTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new WMV3();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use FFMpeg\Format\Video\WMV;
|
||||
|
||||
class WMVTest extends VideoTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new WMV();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use FFMpeg\Format\Video\WebM;
|
||||
|
||||
class WebMTest extends VideoTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new WebM();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Format\Video;
|
||||
|
||||
use FFMpeg\Format\Video\X264;
|
||||
|
||||
class X264Test extends VideoTestCase
|
||||
{
|
||||
public function getFormat()
|
||||
{
|
||||
return new X264();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
use Tests\FFMpeg\Unit\TestCase;
|
||||
|
||||
abstract class AbstractMediaTestCase extends TestCase
|
||||
{
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
abstract class AbstractStreamableTestCase extends AbstractMediaTestCase
|
||||
{
|
||||
public function testGetStreams()
|
||||
{
|
||||
$classname = $this->getClassName();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$format = $this->getFormatMock();
|
||||
|
||||
$ffprobe->expects($this->once())
|
||||
->method('format')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue($format));
|
||||
|
||||
$media = new $classname(__FILE__, $this->getFFMpegDriverMock(), $ffprobe);
|
||||
$this->assertSame($format, $media->getFormat());
|
||||
}
|
||||
|
||||
public function testGetFormat()
|
||||
{
|
||||
$classname = $this->getClassName();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$streams = $this->getStreamCollectionMock();
|
||||
|
||||
$ffprobe->expects($this->once())
|
||||
->method('streams')
|
||||
->with(__FILE__)
|
||||
->will($this->returnValue($streams));
|
||||
|
||||
$media = new $classname(__FILE__, $this->getFFMpegDriverMock(), $ffprobe);
|
||||
$this->assertSame($streams, $media->getStreams());
|
||||
}
|
||||
|
||||
abstract protected function getClassName();
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
use FFMpeg\Media\AdvancedMedia;
|
||||
|
||||
class AdvancedMediaTest extends AbstractMediaTestCase
|
||||
{
|
||||
public function testGetInputs()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertSame([__FILE__, __FILE__], $advancedMedia->getInputs());
|
||||
}
|
||||
|
||||
public function testGetInputsCount()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertEquals(2, $advancedMedia->getInputsCount());
|
||||
}
|
||||
|
||||
public function testFiltersReturnFilters()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertInstanceOf('FFMpeg\Filters\AdvancedMedia\ComplexFilters', $advancedMedia->filters());
|
||||
}
|
||||
|
||||
public function testGetTemporaryDirectoryWithoutCustomConfiguration()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$configuration = $this->getConfigurationMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$configuration->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->equalTo('temporary_directory'))
|
||||
->will($this->returnValue(null));
|
||||
|
||||
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertEquals('', $advancedMedia->getTemporaryDirectory()->path());
|
||||
}
|
||||
|
||||
public function testGetTemporaryDirectoryWithCustomConfiguration()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$configuration = $this->getConfigurationMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$configuration->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->equalTo('temporary_directory'))
|
||||
->will($this->returnValue('/var/ffmpeg'));
|
||||
|
||||
$advancedMedia = new AdvancedMedia([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertEquals('/var/ffmpeg', $advancedMedia->getTemporaryDirectory()->path());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
use FFMpeg\Format\AudioInterface;
|
||||
use FFMpeg\Format\ProgressableInterface;
|
||||
|
||||
abstract class AudioProg implements ProgressableInterface, AudioInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -1,341 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
use FFMpeg\Exception\RuntimeException;
|
||||
use FFMpeg\Media\Audio;
|
||||
|
||||
class AudioTest extends AbstractStreamableTestCase
|
||||
{
|
||||
public function testFiltersReturnsAudioFilters()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
$this->assertInstanceOf('FFMpeg\Filters\Audio\AudioFilters', $audio->filters());
|
||||
}
|
||||
|
||||
public function testAddFiltersAddsAFilter()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
$audio->setFiltersCollection($filters);
|
||||
|
||||
$filter = $this->getMockBuilder('FFMpeg\Filters\Audio\AudioFilterInterface')->getMock();
|
||||
|
||||
$filters->expects($this->once())
|
||||
->method('add')
|
||||
->with($filter);
|
||||
|
||||
$audio->addFilter($filter);
|
||||
}
|
||||
|
||||
public function testAddAVideoFilterThrowsException()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
$audio->setFiltersCollection($filters);
|
||||
|
||||
$filter = $this->getMockBuilder('FFMpeg\Filters\Video\VideoFilterInterface')->getMock();
|
||||
|
||||
$filters->expects($this->never())
|
||||
->method('add');
|
||||
|
||||
$this->expectException('\FFMpeg\Exception\InvalidArgumentException');
|
||||
$audio->addFilter($filter);
|
||||
}
|
||||
|
||||
public function testSaveWithFailure()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$outputPathfile = '/target/file';
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$failure = new RuntimeException('failed to encode');
|
||||
$driver->expects($this->once())
|
||||
->method('command')
|
||||
->will($this->throwException($failure));
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
$this->expectException('\FFMpeg\Exception\RuntimeException');
|
||||
$audio->save($format, $outputPathfile);
|
||||
}
|
||||
|
||||
public function testSaveAppliesFilters()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$outputPathfile = '/target/file';
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
|
||||
$filter = $this->getMockBuilder('FFMpeg\Filters\Audio\AudioFilterInterface')->getMock();
|
||||
$filter->expects($this->once())
|
||||
->method('apply')
|
||||
->with($audio, $format)
|
||||
->will($this->returnValue(['extra-filter-command']));
|
||||
|
||||
$capturedCommands = [];
|
||||
|
||||
$driver->expects($this->once())
|
||||
->method('command')
|
||||
->with($this->isType('array'), false, $this->anything())
|
||||
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands) {
|
||||
$capturedCommands[] = $commands;
|
||||
}));
|
||||
|
||||
$audio->addFilter($filter);
|
||||
$audio->save($format, $outputPathfile);
|
||||
|
||||
foreach ($capturedCommands as $commands) {
|
||||
$this->assertEquals('-y', $commands[0]);
|
||||
$this->assertEquals('-i', $commands[1]);
|
||||
$this->assertEquals(__FILE__, $commands[2]);
|
||||
$this->assertEquals('extra-filter-command', $commands[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSaveData
|
||||
*/
|
||||
public function testSaveShouldSave($threads, $expectedCommands, $expectedListeners, $format)
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$configuration->expects($this->once())
|
||||
->method('has')
|
||||
->with($this->equalTo('ffmpeg.threads'))
|
||||
->will($this->returnValue($threads));
|
||||
|
||||
if ($threads) {
|
||||
$configuration->expects($this->once())
|
||||
->method('get')
|
||||
->with($this->equalTo('ffmpeg.threads'))
|
||||
->will($this->returnValue(24));
|
||||
} else {
|
||||
$configuration->expects($this->never())
|
||||
->method('get');
|
||||
}
|
||||
|
||||
$capturedCommand = $capturedListeners = null;
|
||||
|
||||
$driver->expects($this->once())
|
||||
->method('command')
|
||||
->with($this->isType('array'), false, $this->anything())
|
||||
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommand, &$capturedListeners) {
|
||||
$capturedCommand = $commands;
|
||||
$capturedListeners = $listeners;
|
||||
}));
|
||||
|
||||
$outputPathfile = '/target/file';
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
$audio->save($format, $outputPathfile);
|
||||
|
||||
$this->assertEquals($expectedCommands, $capturedCommand);
|
||||
$this->assertEquals($expectedListeners, $capturedListeners);
|
||||
}
|
||||
|
||||
public function provideSaveData()
|
||||
{
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue([]));
|
||||
$format->expects($this->any())
|
||||
->method('getAudioKiloBitrate')
|
||||
->will($this->returnValue(663));
|
||||
$format->expects($this->any())
|
||||
->method('getAudioChannels')
|
||||
->will($this->returnValue(5));
|
||||
|
||||
$audioFormat = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
$audioFormat->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue([]));
|
||||
$audioFormat->expects($this->any())
|
||||
->method('getAudioKiloBitrate')
|
||||
->will($this->returnValue(664));
|
||||
$audioFormat->expects($this->any())
|
||||
->method('getAudioChannels')
|
||||
->will($this->returnValue(5));
|
||||
$audioFormat->expects($this->any())
|
||||
->method('getAudioCodec')
|
||||
->will($this->returnValue('patati-patata-audio'));
|
||||
|
||||
$formatExtra = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
$formatExtra->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue(['extra', 'param']));
|
||||
$formatExtra->expects($this->any())
|
||||
->method('getAudioKiloBitrate')
|
||||
->will($this->returnValue(665));
|
||||
$formatExtra->expects($this->any())
|
||||
->method('getAudioChannels')
|
||||
->will($this->returnValue(5));
|
||||
|
||||
$listeners = [$this->getMockBuilder('Alchemy\BinaryDriver\Listeners\ListenerInterface')->getMock()];
|
||||
|
||||
$progressableFormat = $this->getMockBuilder('Tests\FFMpeg\Unit\Media\AudioProg')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$progressableFormat->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue([]));
|
||||
$progressableFormat->expects($this->any())
|
||||
->method('createProgressListener')
|
||||
->will($this->returnValue($listeners));
|
||||
$progressableFormat->expects($this->any())
|
||||
->method('getAudioKiloBitrate')
|
||||
->will($this->returnValue(666));
|
||||
$progressableFormat->expects($this->any())
|
||||
->method('getAudioChannels')
|
||||
->will($this->returnValue(5));
|
||||
|
||||
return [
|
||||
[false, [
|
||||
'-y', '-i', __FILE__,
|
||||
'-b:a', '663k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], null, $format],
|
||||
[false, [
|
||||
'-y', '-i', __FILE__,
|
||||
'-acodec', 'patati-patata-audio',
|
||||
'-b:a', '664k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], null, $audioFormat],
|
||||
[false, [
|
||||
'-y', '-i', __FILE__,
|
||||
'extra', 'param',
|
||||
'-b:a', '665k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], null, $formatExtra],
|
||||
[true, [
|
||||
'-y', '-i', __FILE__,
|
||||
'-threads', 24,
|
||||
'-b:a', '663k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], null, $format],
|
||||
[true, [
|
||||
'-y', '-i', __FILE__,
|
||||
'extra', 'param',
|
||||
'-threads', 24,
|
||||
'-b:a', '665k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], null, $formatExtra],
|
||||
[false, [
|
||||
'-y', '-i', __FILE__,
|
||||
'-b:a', '666k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], $listeners, $progressableFormat],
|
||||
[true, [
|
||||
'-y', '-i', __FILE__,
|
||||
'-threads', 24,
|
||||
'-b:a', '666k',
|
||||
'-ac', '5',
|
||||
'/target/file',
|
||||
], $listeners, $progressableFormat],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSaveShouldNotStoreCodecFiltersInTheMedia()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$configuration->expects($this->any())
|
||||
->method('has')
|
||||
->with($this->equalTo('ffmpeg.threads'))
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$configuration->expects($this->any())
|
||||
->method('get')
|
||||
->with($this->equalTo('ffmpeg.threads'))
|
||||
->will($this->returnValue(24));
|
||||
|
||||
$capturedCommands = [];
|
||||
|
||||
$driver->expects($this->exactly(2))
|
||||
->method('command')
|
||||
->with($this->isType('array'), false, $this->anything())
|
||||
->will($this->returnCallback(function ($commands, $errors, $listeners) use (&$capturedCommands, &$capturedListeners) {
|
||||
$capturedCommands[] = $commands;
|
||||
}));
|
||||
|
||||
$outputPathfile = '/target/file';
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\AudioInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue(['param']));
|
||||
|
||||
$audio = new Audio(__FILE__, $driver, $ffprobe);
|
||||
$audio->save($format, $outputPathfile);
|
||||
$audio->save($format, $outputPathfile);
|
||||
|
||||
$expected = [
|
||||
'-y', '-i', __FILE__, 'param', '-threads', 24, '/target/file',
|
||||
];
|
||||
|
||||
foreach ($capturedCommands as $capturedCommand) {
|
||||
$this->assertEquals($expected, $capturedCommand);
|
||||
}
|
||||
}
|
||||
|
||||
public function getClassName()
|
||||
{
|
||||
return 'FFMpeg\Media\Audio';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
use FFMpeg\Media\Clip;
|
||||
|
||||
class ClipTest extends AbstractMediaTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideBuildOptions
|
||||
*/
|
||||
public function testBuildCommand($startValue, $durationValue, $commands)
|
||||
{
|
||||
$configuration = $this->getConfigurationMock();
|
||||
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$start = $this->getTimeCodeMock();
|
||||
$start->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue($startValue));
|
||||
|
||||
$duration = null;
|
||||
if (null !== $durationValue) {
|
||||
$duration = $this->getTimeCodeMock();
|
||||
$duration->expects($this->once())
|
||||
->method('__toString')
|
||||
->will($this->returnValue($durationValue));
|
||||
}
|
||||
|
||||
$outputPathfile = '/target/file';
|
||||
|
||||
$format = $this->getMockBuilder('FFMpeg\Format\VideoInterface')->getMock();
|
||||
$format->expects($this->any())
|
||||
->method('getPasses')
|
||||
->will($this->returnValue(1));
|
||||
$format->expects($this->any())
|
||||
->method('getExtraParams')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$clip = new Clip($this->getVideoMock(__FILE__), $driver, $ffprobe, $start, $duration);
|
||||
$fc = $clip->getFinalCommand($format, $outputPathfile);
|
||||
|
||||
$this->assertCount(1, $fc);
|
||||
$this->assertStringStartsWith(implode(' ', $commands), $fc[0]);
|
||||
}
|
||||
|
||||
public function provideBuildOptions()
|
||||
{
|
||||
return [
|
||||
['SS01', null, [
|
||||
'-y', '-ss', 'SS01',
|
||||
'-i', __FILE__, ],
|
||||
],
|
||||
['SS02', 'D02', [
|
||||
'-y', '-ss', 'SS02',
|
||||
'-i', __FILE__,
|
||||
'-t', 'D02', ],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\FFMpeg\Unit\Media;
|
||||
|
||||
use FFMpeg\Media\Concat;
|
||||
use Mockery;
|
||||
use Spatie\TemporaryDirectory\TemporaryDirectory;
|
||||
|
||||
class ConcatTest extends AbstractMediaTestCase
|
||||
{
|
||||
public function testGetSources()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$concat = new Concat([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertSame([__FILE__, __FILE__], $concat->getSources());
|
||||
}
|
||||
|
||||
public function testFiltersReturnFilters()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$concat = new Concat([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$this->assertInstanceOf('FFMpeg\Filters\Concat\ConcatFilters', $concat->filters());
|
||||
}
|
||||
|
||||
public function testAddFiltersAddsAFilter()
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$filters = $this->getMockBuilder('FFMpeg\Filters\FiltersCollection')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$filter = $this->getMockBuilder('FFMpeg\Filters\Concat\ConcatFilterInterface')->getMock();
|
||||
|
||||
$filters->expects($this->once())
|
||||
->method('add')
|
||||
->with($filter);
|
||||
|
||||
$concat = new Concat([__FILE__, __FILE__], $driver, $ffprobe);
|
||||
$concat->setFiltersCollection($filters);
|
||||
$concat->addFilter($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSaveFromSameCodecsOptions
|
||||
*/
|
||||
public function testSaveFromSameCodecs($streamCopy, $commands)
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
|
||||
$pathfile = '/target/destination';
|
||||
|
||||
array_push($commands, $pathfile);
|
||||
|
||||
$configuration = Mockery::mock('Alchemy\BinaryDriver\ConfigurationInterface');
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$configuration->shouldReceive('get')
|
||||
->once()
|
||||
->with('temporary_directory')
|
||||
->andReturnNull();
|
||||
|
||||
$driver->expects($this->exactly(1))
|
||||
->method('command')
|
||||
->with($this->isType('array'), false, $this->anything())
|
||||
->will($this->returnCallback(function ($commands, $errors, $listeners) {
|
||||
}));
|
||||
|
||||
$concat = new Concat([__FILE__, 'concat-2.mp4'], $driver, $ffprobe);
|
||||
$concat->saveFromSameCodecs($pathfile, $streamCopy);
|
||||
|
||||
$this->assertEquals('-f', $commands[0]);
|
||||
$this->assertEquals('concat', $commands[1]);
|
||||
$this->assertEquals('-safe', $commands[2]);
|
||||
$this->assertEquals('0', $commands[3]);
|
||||
$this->assertEquals('-i', $commands[4]);
|
||||
if (isset($commands[6]) && (0 == strcmp($commands[6], '-c'))) {
|
||||
$this->assertEquals('-c', $commands[6]);
|
||||
$this->assertEquals('copy', $commands[7]);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideSaveFromSameCodecsOptions()
|
||||
{
|
||||
$fs = (new TemporaryDirectory())->create();
|
||||
$tmpFile = $fs->path('ffmpeg-concat');
|
||||
touch($tmpFile);
|
||||
|
||||
return [
|
||||
[
|
||||
true,
|
||||
[
|
||||
'-f', 'concat',
|
||||
'-safe', '0',
|
||||
'-i', $tmpFile,
|
||||
'-c', 'copy',
|
||||
],
|
||||
],
|
||||
[
|
||||
false,
|
||||
[
|
||||
'-f', 'concat',
|
||||
'-safe', '0',
|
||||
'-i', $tmpFile,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideSaveFromDifferentCodecsOptions
|
||||
*/
|
||||
public function testSaveFromDifferentCodecs($commands)
|
||||
{
|
||||
$driver = $this->getFFMpegDriverMock();
|
||||
$ffprobe = $this->getFFProbeMock();
|
||||
$format = $this->getFormatInterfaceMock();
|
||||
|
||||
$pathfile = '/target/destination';
|
||||
|
||||
array_push($commands, $pathfile);
|
||||
|
||||
$configuration = $this->getMockBuilder('Alchemy\BinaryDriver\ConfigurationInterface')->getMock();
|
||||
|
||||
$driver->expects($this->any())
|
||||
->method('getConfiguration')
|
||||
->will($this->returnValue($configuration));
|
||||
|
||||
$driver->expects($this->once())
|
||||
->method('command')
|
||||
->with($commands);
|
||||
|
||||
$concat = new Concat([__FILE__, 'concat-2.mp4'], $driver, $ffprobe);
|
||||
$this->assertSame($concat, $concat->saveFromDifferentCodecs($format, $pathfile));
|
||||
}
|
||||
|
||||
public function provideSaveFromDifferentCodecsOptions()
|
||||
{
|
||||
return [
|
||||
[
|
||||
[
|
||||
'-i', __FILE__,
|
||||
'-i', 'concat-2.mp4',
|
||||
'-filter_complex',
|
||||
'[0:v:0] [0:a:0] [1:v:0] [1:a:0] concat=n=2:v=1:a=1 [v] [a]',
|
||||
'-map', '[v]',
|
||||
'-map', '[a]',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue