/ATTIC/video-contact-sheet/trunk/.github/workflows/ci-lint.yaml |
---|
0,0 → 1,43 |
--- |
name: Run linter |
on: |
push: |
paths: |
- dist/vcs |
- '*/workflows/*' |
pull_request: |
workflow_dispatch: # will allow running manually |
jobs: |
build: |
runs-on: ubuntu-20.04 |
env: |
shellcheck_tarball: shellcheck-stable.linux.x86_64.tar.xz |
shellcheck_url: https://github.com/koalaman/shellcheck/releases/download/stable/${{ env.shellcheck_tarball }} |
steps: |
# Use the latest stable version directly instead of downstream |
#- run: apt-get update -y && apt-get install -y --no-install-recommends shellcheck |
#- run: | |
# if ! type -p wget || ! type -p xz ; then |
# apt-get update -y && apt-get install -y --no-install-recommends wget xz-utils ; |
# fi |
- run: wget ${{ env.shellcheck_url }} |
- run: tar xvf ${{ env.shellcheck_tarball }} |
# After checkout we won't necessarily be on the same directory |
- name: Ensure shellcheck is accessible |
run: test -x shellcheck-stable/shellcheck && mv shellcheck-stable/shellcheck /usr/bin/ |
- name: Checkout repository |
uses: actions/checkout@v3 |
# Provide alternative formats, the second one is more readable but doesn't display as |
# well on the web UI |
- name: Validate script (short) |
continue-on-error: true |
run: shellcheck -f gcc vcs |
- name: Validate script (verbose) |
run: shellcheck vcs |
/ATTIC/video-contact-sheet/trunk/LICENSE |
---|
0,0 → 1,175 |
GNU LESSER GENERAL PUBLIC LICENSE |
Version 2.1, February 1999 |
Copyright (C) 1991, 1999 Free Software Foundation, Inc. |
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. |
[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] |
Preamble |
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. |
This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. |
When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. |
To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. |
For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. |
We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. |
To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. |
Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. |
Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. |
When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. |
We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. |
For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. |
In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. |
Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. |
The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. |
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". |
A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. |
The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) |
"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. |
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. |
1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. |
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. |
2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: |
a) The modified work must itself be a software library. |
b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. |
c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. |
d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. |
(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) |
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. |
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. |
In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. |
3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. |
Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. |
This option is useful when you wish to copy part of the code of the Library into a program that is not a library. |
4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. |
If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. |
5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. |
However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. |
When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. |
If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) |
Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. |
6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. |
You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: |
a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) |
b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. |
c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. |
d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. |
e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. |
For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. |
It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. |
7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: |
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. |
b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. |
8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. |
9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. |
10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. |
11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. |
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. |
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. |
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. |
12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. |
13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. |
Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. |
14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. |
NO WARRANTY |
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. |
END OF TERMS AND CONDITIONS |
How to Apply These Terms to Your New Libraries |
If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). |
To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. |
one line to give the library's name and an idea of what it does. |
Copyright (C) year name of author |
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. |
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. |
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: |
Yoyodyne, Inc., hereby disclaims all copyright interest in |
the library `Frob' (a library for tweaking knobs) written |
by James Random Hacker. |
signature of Ty Coon, 1 April 1990 |
Ty Coon, President of Vice |
That's all there is to it! |
/ATTIC/video-contact-sheet/trunk/dist/vcs |
---|
0,0 → 1,5361 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007-2022 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.5" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=1 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
# Starting with Bash 5 uppercase $COLUMNS can't be safely set in the script. |
declare -i PADDING=2 |
declare -i NUM_COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"NUM_COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"COLUMNS:NUM_COLUMNS:alias:p" # Up to 1.13.3 |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_NUMCAPS' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
# Sizes are always rounded up (hence the addition 0.999999 to the fractionary part) |
# gawk understands the ** operator, but mawk does not, using precomputed |
# values for the sake of compatibility |
declare -ri GBS=$(( 1024**3 )) |
declare -ri MBS=$(( 1024**2 )) |
if [[ $bytes -gt $GBS ]]; then |
local gibs_int=$(( $bytes / $GBS )) |
local gibs_frac=$(awkex "int($bytes%$GBS*100/$GBS + 0.999999)" ) |
size="$(printf '%d.%02d' $gibs_int $gibs_frac) GiB" |
elif [[ $bytes -gt $MBS ]]; then |
local mibs_int=$(( $bytes / $MBS )) |
local mibs_frac=$(awkex "int($bytes%$MBS*100/$MBS + 0.999999)") |
size="$(printf '%d.%02d' $mibs_int $mibs_frac) MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs_int=$(( $bytes / 1024 )) |
local kibs_frac=$(awkex "int($bytes%1024*100/1024 + 0.999999)") |
size="$(printf '%d.%02d' $kibs_int $kibs_frac) KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# First we must find the correct way to query color support. |
# There's basically two variants of tput: |
# terminfo (Linux) and termcap (FreeBSD) |
# These is an issue for portability: |
# - On Linux 'tput colors' is used to query it |
# - On FreeBSD 'tput Co' is used to query it |
# - Linux's tput will fail if it's passed 'Co' |
# - FreeBSD's tput will interpret 'colors' as 'co' and print the number |
# of columns (cols is terminfo's equivalent) |
local tputc="-1" |
if tput Co >/dev/null ; then |
tputc=$(tput Co) # termcap style |
else |
# Try to guess if it's parsing it as columns |
# The method here is to check against some known terminals |
# - pilot: 39 columns mono. Unknown on (some?) Linuxes |
# - pc3: 80 columns, 8 colors. Unknown on some Linuxes |
# - vt100: 80 columns, mono: |
# On FreeBSD: |
# Co fails silently on FreeBSD, colors (interpreted as columns) prints 80, |
# On Linux colors prints -1, columns prints 80 |
# NOTE: If the terminal type is unknown tput will error with a message |
if [[ '80' != "$(tput -T vt100 colors)" ]] ; then |
# colors is interpreted literally |
tputc=$(tput colors) |
fi |
fi 2>/dev/null |
# Is it able to set colours? |
# Linux's tput can be passed arguments to retrieve the correct escape sequences |
# FreeBSD's tput can not |
if tput bold && [[ "-1" != "$tputc" ]] && tput setaf 0 && tput sgr0; then |
# Can configure completely through tput |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
elif [[ "-1" != "$tputc" ]]; then |
# tput reports color support but it doesn't provide |
# the escape codes directly, will use hardcoded escape codes instead |
HAS_COLORS= |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# NOTE: These messages are supressed if called from classic_identify |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in ${EVASION_ALTERNATIVES[@]} ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
RESULT= |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} || { |
# Failed capture |
return $? |
} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)." |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
# NOTE I was using image.mean up to 1.13.4, but that fails on |
# ImageMagick 7.1. mean seems to be supported in old versions as per |
# https://web.archive.org/web/20080323173044/https://imagemagick.org/script/escape.php |
# https://web.archive.org/web/20140416184234/https://imagemagick.org/script/escape.php |
# and in fact image.mean isn't listed there |
# The output is exactly the same with image.mean and mean. |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
# Allow both the synthetic (for older mplayer) and builtin (for newer mplayer) codec ids |
vcs_hevc|HEVC) vcodec="HEVC" ;; |
vcs_vp9|VP90) vcodec="VP9" ;; # VP9 was detected as rawyuy2 by older MPlayer |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# HEVC and VP9 weren't supported by older versions MPlayer |
# Seen: |
# "hevc (Main) (HEVC / 0x43564548)" in MPEG2-TS |
# "hevc" in h.265 ES |
# TODO: Enforce a minimum version of mplayer |
hevc|hevc\ *) mpid="vcs_hevc" ;; |
vp9) mpid="vcs_vp9" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# and same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
# If the decoder can't seek there's no point in continuing |
if [[ ( ( $DECODER -eq $DEC_FFMPEG ) && ( -z $fflen ) ) || |
( ( $DECODER -eq $DEC_MPLAYER ) && ( -z $mplen ) ) ]]; |
then |
warn "$CAPTURER didn't report a length, seeking won't be possible." |
return $RET_NOLEN |
fi |
[[ -z $fflen ]] && fflen=0 |
[[ -z $mplen ]] && mplen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || { |
# Fall back to ffmpeg's dimensions |
VID[$W]=${FFMPEG_ID[$W]} |
VID[$H]=${FFMPEG_ID[$H]} |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
# MPlayer tends to identify as raw video if it doesn't support the codec |
# fall back to FFmpeg in such case |
if [[ ${MPLAYER_ID[$VCODEC]} = "0x00000000" ]]; then |
VID[$VCODEC]=${FFMPEG_ID[$VCODEC]} |
VID[$VCNAME]=${FFMPEG_ID[$VCNAME]} |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
warn " Does $CAPTURER support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
local ret=$? |
VID=( "${RESULT[@]}" ) |
return $ret |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
local mpplen= |
[[ -z "${MPLAYER_ID[${LEN}]}" ]] || mpplen=$(pretty_stamp ${MPLAYER_ID[$LEN]}) |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $mpplen |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
local clen= |
[[ -z ${VID[$LEN]} ]] || clen=$(pretty_stamp ${VID[$LEN]}) |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $clen |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $NUM_COLUMNS ]]; then |
numcols=$n |
else |
numcols=$NUM_COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || { |
exitcode=$? |
[[ ${#capfiles[@]} -gt 0 ]] || { |
# No successful capture, unsupported format? |
# TODO: Adapt to capturer in use |
warn "No successful capture, possible unsupported format." |
} |
return $exitcode |
} |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $NUM_COLUMNS ]]; then |
numcols=$n |
else |
numcols=$NUM_COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'NUM_COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $NUM_COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
# File size rounding |
"get_pretty_size 1127428915 1.05%20GiB #Leading zeroes in file size (GiB)" |
"get_pretty_size 1132462 1.08%20MiB #Leading zeroes in file size (MiB)" |
"get_pretty_size 1116 1.09%20KiB #Leading zeroes in file size (KiB)" |
"get_pretty_size 1889785610 1.76%20GiB #Pretty-printed file size (GiB)" |
"get_pretty_size 762650296 727.32%20MiB #Pretty-printed file size (MiB)" |
"get_pretty_size 524810 512.51%20KiB #Pretty-printed file size (KiB)" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
val=${val/\%20/ } |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Expected '$val'. Got '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2022 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
NOTE: If you have any configuration loaded before this |
takes effect the script might still print some |
colour. You can disable it completely by setting |
the TERM variable to a monochrome term type, e.g.: |
$ env TERM=vt100 vcs [options] |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
NUM_COLUMNS="$2" |
USR_NUM_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
if type -paf lsb_release >/dev/null ; then |
lsb_release=$(lsb_release -d | cut -d: -f2- | sed 's/^[[:space:]]*//') |
fi |
imversion=$(convert --version | head -1 | cut -d' ' -f2-) |
if [[ -n "$MPLAYER_BIN" ]]; then |
mpversion=$("$MPLAYER_BIN" --version 2>/dev/null || true) |
# Older mplayer doesn't understand --version... |
if grep "Unknown option" <<<"$mpversion" ; then |
# ...But the last output line contains the version in my sample |
mpversion=$(tail -1 <<<"$mpversion") |
fi |
fi |
if [[ -n "$FFMPEG_BIN" ]]; then |
# Older versions print to stderr, newer to stdout |
ffversion=$("$FFMPEG_BIN" -version 2>&1 | head -1) |
lavcversion=$("$FFMPEG_BIN" -version 2>&1 | grep libavcodec \ |
| sed 's/[[:space:]][[:space:]]*/ /') |
ffversion="$ffversion / $lavcversion" |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
MPlayer: $mpversion |
FFMpeg: $ffversion |
ImageMagick: $imversion |
LSB Description: $lsb_release |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/CHANGELOG |
---|
0,0 → 1,535 |
1.13.5 (?): |
* BUGFIX: Fix error during blank frame evasion on newer ImageMagick [#378] |
* BUGFIX: Fix constraint check error message |
* BUGFIX: Fix terminal capabilities check to enable color output on more |
systems |
1.13.4 (2019-11-26): |
* BUGFIX: Actually use all alternative evasion offsets (Bugfix by Davide |
Cavestro) [#364] |
* BUGFIX: Display file sizes correctly when using mawk [#365] |
* BUGFIX: Number of columns ignored on Bash 5.0 [#373] |
1.13.3 (2017-05-26): |
* Added codec IDs for h.265 and VP9 |
* BUGFIX: Fix handling of failed captures |
* BUGFIX: Fix handling of failed identification |
* BUGFIX: Cleaned output for identification of unsupported file types |
* BUGFIX: Codec information was getting cropped with current versions of |
ImageMagick. Gravity appears to be interpreted in a different way |
now. (Bugfix by Markus) [#323] |
* BUGFIX: Fix incorrect calculation of file size in header. (Based on an |
anonymous patch) [#314] |
* OTHER: Print warning about possible lack of support if no frame could be |
captured |
* OTHER: Don't trust MPlayer's detection of raw video, use FFmpeg's |
detection in such case |
* OTHER: Fix incorrect rendering of Note #1 in vcs.conf's manpage |
* OTHER: Clean up generation and conversion of manpages |
* OTHER: Added versions of MPlayer, FFMpeg, ImageMagick and LSB to debug |
output |
* OTHER: Allow disabling coloured output altogether. [#311] |
This is implemented by honouring $TERM, e.g. "TERM=vt100 vcs ..." |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/debian/changelog |
---|
0,0 → 1,117 |
vcs (1.13.4-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Tue, 26 Nov 2019 11:53:05 +0100 |
vcs (1.13.3-pon.1) experimental; urgency=medium |
* New version |
* debian/control: Added xsltproc to Build-Depends |
* debian/compat: Bumped compatibility level to 9 (jessie) |
* debian/control: Bumped build-dependancy on debhelper to >= 9 |
(compatibility level 9) |
-- Toni Corvera <outlyer@gmail.com> Sat, 20 May 2017 00:04:51 +0200 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/trunk/dist/debian/source/format |
---|
0,0 → 1,0 |
3.0 (quilt) |
/ATTIC/video-contact-sheet/trunk/dist/debian/compat |
---|
0,0 → 1,0 |
9 |
/ATTIC/video-contact-sheet/trunk/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 9), xsltproc |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/trunk/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/trunk/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/trunk/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/trunk/dist/rpm/rpmlint.conf |
---|
0,0 → 1,2 |
addFilter("E:.* no-signature") |
/ATTIC/video-contact-sheet/trunk/dist/rpm/references |
---|
0,0 → 1,21 |
Some useful references: |
"Creating RPM Packages with Fedora" |
<https://fedoraproject.org/wiki/How_to_create_an_RPM_package> |
"Packaging for openSUSE Leap" |
<https://en.opensuse.org/openSUSE:How_to_contribute_to_Leap> |
"Build Service cross distribution howto" |
<https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto> |
"Fedora packaging guidelines" |
<https://fedoraproject.org/wiki/Packaging:Guidelines> |
Alternative requirements: |
As of 2017 there's some conflicting information on boolean operators |
[1] says they are not supported in Requires |
[2] says they are supported in all fields, including requires (rpm >= 4.13) |
1: <https://fedoraproject.org/wiki/Packaging:Guidelines#Rich.2FBoolean_dependencies> |
2: <http://rpm.org/user_doc/boolean_dependencies.html> |
Fedora 25 has RPM 4.13 |
openSUSE Leap 42.2 has RPM 4.11 |
/ATTIC/video-contact-sheet/trunk/dist/rpm/vcs.spec.in |
---|
0,0 → 1,124 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# originally based on mp3plot's which in turn was based on other sources |
%define is_suse 0%{?suse_version} |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPLv2+ |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
#Requires: rpm >= 4.13 |
#Requires: ( mplayer or ffmpeg ) |
Requires: ffmpeg |
Recommends: mplayer |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
# as per rpmlint: E: wrong-script-interpreter /usr/bin/vcs /usr/bin/env bash |
sed -i '1s@.*@#!/bin/bash@' %buildroot/usr/bin/vcs |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat May 20 2017 - outlyer (at) gmail (dot) com |
- Rewrote dependencies with notes about boolean (alternative) dependecies |
- Depend on ffmpeg and recommend mplayer while distributions catch up with RPM |
- Removed Mandrake detection and updated the macro for SUSE |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/common.mk |
---|
0,0 → 1,101 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
# handles .tar.gz, .tar.bz2 and .tar.xz |
vcs-$(VERSION).tar: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar cf $@ vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
vcs-$(VERSION).tar.gz: vcs-$(VERSION).tar |
gzip -9 $< |
vcs-$(VERSION).tar.bz2: vcs-$(VERSION).tar |
bzip2 -9 $< |
vcs-$(VERSION).tar.xz: vcs-$(VERSION).tar |
xz -9 $< |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/docs/src/plain_messages_note.man.inc.xml |
---|
0,0 → 1,16 |
<!DOCTYPE para PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
<!-- This file is meant to be included, it contains the explanation |
about handling of colourised (console) output, to be shared |
by both the documentation of -Wc and plain_messages. --> |
<para> |
<note> |
<para>Some colour will be printed by default until this option is handled. |
If you need to completely disable colour, e.g. to run in cron jobs, you can |
do so by setting an appropriate TERM environment variable e.g. |
</para> |
<!-- Note: literallayout allows injection of linebreaks (Docbook has no <br /> but HTML output doesn't come out too pretty--> |
<para><literal>$ <command>TERM=<replaceable>vt100</replaceable> vcs</command></literal></para> |
<para>will make the script switch to monochrome output altogether.</para> |
</note> |
</para> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,594 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date$ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,1085 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5.1: The Definitive Guide (includes list of elements) |
<http://tdg.docbook.org/tdg/5.1/> |
- ": refentry - A reference page (originally a UNIX man-style reference page). |
<http://tdg.docbook.org/tdg/5.1/refentry.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man ./vcs.1 | less |
or |
$ man ./vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). |
"Video Contact Sheet *NIX User Manual" is too long and |
"Manual" is cropped off --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
<!-- for vcs.conf(5) --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY confpackage "vcs.conf"> |
<!ENTITY confsection "5"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!-- END OF DOCTYPE --> |
<reference><!-- Group of refentry's --> |
<!-- This is required by reference to validate with e.g. xmlto. |
NOTE This appears to override the title set below. --> |
<title>&title;</title> |
<!-- START OF man(1) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<date>Last revision: 2017-05-23</date><!-- Exported on: $Date$ --> |
<revhistory> |
<revision> |
<date>2017-05-23</date> |
<revremark>Added note about disabling colour output (starting with 1.13.3).</revremark> |
</revision> |
<revision> |
<date>2011-08-29</date> |
</revision> |
</revhistory> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="option_wc"><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
</listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><envar>TERM</envar></term> |
<listitem> |
<para>Affects the usage of colour output to console being on or off |
by default. See the documentation for <option><xref linkend="option_wc" /></option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- END OF vcs(1) --> |
<!-- START OF vcs.conf(5) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&confpackage;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
<date>Last revision: 2011-08-29</date> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&confpackage;</refentrytitle> |
<manvolnum>&confsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&confpackage;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<!-- Note: \[char46] is a escaped dot for groff. |
Otherwise this paragraph's output will start a line |
with a dot, which makes groff/man interpret it as an |
inexistent macro, |
i.e. the filename won't render at all |
Reference: http://stackoverflow.com/questions/11469341/ |
--> |
<para><filename>\[char46]/vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- END OF vcs.conf(5) --> |
</reference> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/docs/GNUmakefile |
---|
0,0 → 1,134 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
# Since 1.13.3 the man pages are combined into a single input |
# The XHTML output contains both man pages as a side effect, while the groff |
# and PDF output are separate for each man page. TeX output was removed from |
# this makefile. |
DEFAULT=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .pdf) \ |
$(addprefix vcs.conf.man,.pdf) \ |
) |
EXTRA=$(addprefix $(distdir)/,vcs.man2html.html vcs.conf.man2html.html) |
ALL=$(DEFAULT) $(EXTRA) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_COMMON=xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" |
# NOT: For manpages the output can be a directory, whereas for XHTML it can not |
DOCBOOK_TO_MAN=$(DOCBOOK_TO_COMMON) -o $(distdir)/ $(DOCBOOK_XSL)/manpages/docbook.xsl |
DOCBOOK_TO_XHTML=$(DOCBOOK_TO_COMMON) $(DOCBOOK_XSL)/xhtml/docbook.xsl |
default: $(DEFAULT) |
extra: $(EXTRA) |
all: $(ALL) |
env: |
@echo --- Values of Makefile variables: --- |
@echo DEFAULT: $(DEFAULT) |
@echo EXTRA: $(EXTRA) |
@echo ALL: $(ALL) |
@echo INTERMEDIATE: $(INTERMEDIATE) |
@echo DOCBOOK_XSL: $(DOCBOOK_XSL) |
@echo DOCBOOK_TO_MAN: $$ $(DOCBOOK_TO_MAN) |
@echo DOCBOOK_TO_XHTML: $$ $(DOCBOOK_TO_XHTML) |
@echo distdir: $(distdir) |
@echo srcdir: $(srcdir) |
@echo ------------------------------------- |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# These are both generated at once |
$(distdir)/vcs.1 $(distdir)/vcs.conf.5: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# Note with both manpages combined the output is combined too |
$(distdir)/vcs.man.html: $(srcdir)/vcs.man.xml |
$(DOCBOOK_TO_XHTML) "$<" > "$@" || ( $(RM) "$@" && false ) |
@# sed post processing: |
@# add CSS link |
@# obfuscate mailto: links |
@# obfuscate emails |
@# replace groff-escaped dots ("\[char46]") |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
-e 's/\\\[char46\]/./g' \ |
"$@" |
@# man2html includes the last revision date, which xsltproc does not, it |
@# seems useful to me, so I'm injecting it here |
sed -i \ |
-e 's!</h1>!</h1>$(shell grep 'Last revision' $< | head -1 \ |
| sed -e 's!.*<date>!!' \ |
-e 's!</date>.*$$!!')!' \ |
"$@" |
##### |
##### RULES SHARING RECIPES |
##### See http://stackoverflow.com/questions/11441084/makefile-with-multiples-rules-sharing-same-recipe |
##### |
# 1/2: Pre-requisites |
$(distdir)/vcs.conf.man.pdf: $(distdir)/vcs.conf.5.pdf |
$(distdir)/vcs.man.pdf: $(distdir)/vcs.1.pdf |
$(distdir)/vcs.man2html.html: $(distdir)/vcs.1 |
$(distdir)/vcs.conf.man2html.html: $(distdir)/vcs.conf.5 |
# 2/2: Common recipe |
$(distdir)/vcs.conf.man.pdf $(distdir)/vcs.man.pdf: |
mv $< $@ |
##### |
##### / END OF RULES SHARING RECIPES |
##### |
$(distdir)/vcs.man2html.html $(distdir)/vcs.conf.man2html.html: |
@# The first two lines of man2html are HTTP headers |
@# The manpage is preprocessed to replace groff-escaped dots (\[char46]) |
sed -e 's/\\\[char46\]/\\./g' "$<" | man2html -r | sed 1,2d > "$@" |
# jade and docbook-to-man conversions don't appear to agree on what's the |
# correct structure, to avoid this here I use doclifter to convert back from |
# man to DocBook, which generates a less semantically-rich but easier to |
# process DocBook file |
$(distdir)/vcs.%.pdf: vcs.% |
doclifter < $< > temp.xml || ( $(RM) temp.xml && false ) |
db2pdf temp.xml || ( $(RM) temp.xml && false ) |
-$(RM) temp.xml |
mv temp.pdf $@ |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.html' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.html, $(DEFAULT)) |
.PHONY: all default extra env clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/trunk/dist/arch/PKGBUILD.in |
---|
0,0 → 1,43 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('mplayer: for better more complete detection/capture' |
'lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/trunk/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/trunk/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/trunk/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/GNUmakefile |
---|
0,0 → 1,142 |
#!/usr/bin/make -f |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.%: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.%: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n 'Ignore? [y/N] ' ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
@if ! sed -e '/$$Date/!d' dist/vcs | grep -E 'Mon|Tue|Wed|Thu|Fri|Sat|Sun' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from localised SVN export **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n "Ignore? [y/N] " ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
false ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .xz .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb srpm |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).xz: $(srcdir)/vcs |
xz -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint --file=dist/rpm/rpmlint.conf vcs-$(VER)-*.rpm |
srpm: vcs-$(VER).tar.gz |
rpmbuild --clean -ts vcs-$(VER).tar.gz |
test -d ~/rpmbuild/SRPMS && ln -s ~/rpmbuild/SRPMS/vcs-$(VER)-*.src.rpm . || true |
test -d ~/RPM/SRPMS && ln -s ~/RPM/SRPMS/vcs-$(VER)-*.src.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint vcs-$(VER)-*.src.rpm |
false |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/online_man/Makefile |
---|
0,0 → 1,18 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html |
man.vcs.html: $(docsdir)/vcs.man.html |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
.PHONY: all clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/trunk/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/trunk/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/trunk |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/branches/1.13.2-pre.3+early_color:r664-665 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/tags/1.13.2-pre.4:r666 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/CHANGELOG |
---|
0,0 → 1,529 |
1.13.4 (2019-11-26): |
* BUGFIX: Actually use all alternative evasion offsets (Bugfix by Davide |
Cavestro) [#364] |
* BUGFIX: Display file sizes correctly when using mawk [#365] |
* BUGFIX: Number of columns ignored on Bash 5.0 [#373] |
1.13.3 (2017-05-26): |
* Added codec IDs for h.265 and VP9 |
* BUGFIX: Fix handling of failed captures |
* BUGFIX: Fix handling of failed identification |
* BUGFIX: Cleaned output for identification of unsupported file types |
* BUGFIX: Codec information was getting cropped with current versions of |
ImageMagick. Gravity appears to be interpreted in a different way |
now. (Bugfix by Markus) [#323] |
* BUGFIX: Fix incorrect calculation of file size in header. (Based on an |
anonymous patch) [#314] |
* OTHER: Print warning about possible lack of support if no frame could be |
captured |
* OTHER: Don't trust MPlayer's detection of raw video, use FFmpeg's |
detection in such case |
* OTHER: Fix incorrect rendering of Note #1 in vcs.conf's manpage |
* OTHER: Clean up generation and conversion of manpages |
* OTHER: Added versions of MPlayer, FFMpeg, ImageMagick and LSB to debug |
output |
* OTHER: Allow disabling coloured output altogether. [#311] |
This is implemented by honouring $TERM, e.g. "TERM=vt100 vcs ..." |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/changelog |
---|
0,0 → 1,117 |
vcs (1.13.4-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Tue, 26 Nov 2019 11:53:05 +0100 |
vcs (1.13.3-pon.1) experimental; urgency=medium |
* New version |
* debian/control: Added xsltproc to Build-Depends |
* debian/compat: Bumped compatibility level to 9 (jessie) |
* debian/control: Bumped build-dependancy on debhelper to >= 9 |
(compatibility level 9) |
-- Toni Corvera <outlyer@gmail.com> Sat, 20 May 2017 00:04:51 +0200 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/source/format |
---|
0,0 → 1,0 |
3.0 (quilt) |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/compat |
---|
0,0 → 1,0 |
9 |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 9), xsltproc |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/vcs |
---|
0,0 → 1,5348 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007-2019 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.4" |
declare -r RELEASE=1 |
declare -ri PRERELEASE=2 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
# Starting with Bash 5 uppercase $COLUMNS can't be safely set in the script. |
declare -i PADDING=2 |
declare -i NUM_COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"NUM_COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"COLUMNS:NUM_COLUMNS:alias:p" # Up to 1.13.3 |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
# Sizes are always rounded up (hence the addition 0.999999 to the fractionary part) |
# gawk understands the ** operator, but mawk does not, using precomputed |
# values for the sake of compatibility |
declare -ri GBS=$(( 1024**3 )) |
declare -ri MBS=$(( 1024**2 )) |
if [[ $bytes -gt $GBS ]]; then |
local gibs_int=$(( $bytes / $GBS )) |
local gibs_frac=$(awkex "int($bytes%$GBS*100/$GBS + 0.999999)" ) |
size="$(printf '%d.%02d' $gibs_int $gibs_frac) GiB" |
elif [[ $bytes -gt $MBS ]]; then |
local mibs_int=$(( $bytes / $MBS )) |
local mibs_frac=$(awkex "int($bytes%$MBS*100/$MBS + 0.999999)") |
size="$(printf '%d.%02d' $mibs_int $mibs_frac) MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs_int=$(( $bytes / 1024 )) |
local kibs_frac=$(awkex "int($bytes%1024*100/1024 + 0.999999)") |
size="$(printf '%d.%02d' $kibs_int $kibs_frac) KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# First we must find the correct way to query color support. |
# There's basically two variants of tput: |
# terminfo (Linux) and termcap (FreeBSD) |
# These is an issue for portability: |
# - On Linux 'tput colors' is used to query it |
# - On FreeBSD 'tput Co' is used to query it |
# - Linux's tput will fail if it's passed 'Co' |
# - FreeBSD's tput will interpret 'colors' as 'co' and print the number of columns |
local tputc="-1" |
if tput Co >/dev/null 2>&1 ; then |
tputc=$(tput Co) # termcap style |
else |
# Try to guess if it's parsing it as columns |
# The method here is to check against some known terminals |
# pilot: 39 columns mono, pc3: 80 columns, 8 colors |
if [[ 8 = "$(tput -T pc3 colors)" ]]; then |
# colors is interpreted literally |
tputc=$(tput colors) |
fi |
fi |
# Is it able to set colours? |
# Linux's tput can be passed arguments to retrieve the correct escape sequences |
# FreeBSD's tput can not |
if tput bold && [[ "-1" != "$tputc" ]] && tput setaf 0 && tput sgr0; then |
# Can configure completely through tput |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
elif [[ "-1" != "$tputc" ]]; then |
# tput reports color support but it doesn't provide |
# the escape codes directly, will use hardcoded escape codes instead |
HAS_COLORS= |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# NOTE: These messages are supressed if called from classic_identify |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in ${EVASION_ALTERNATIVES[@]} ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
RESULT= |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} || { |
# Failed capture |
return $? |
} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)." |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
# Allow both the synthetic (for older mplayer) and builtin (for newer mplayer) codec ids |
vcs_hevc|HEVC) vcodec="HEVC" ;; |
vcs_vp9|VP90) vcodec="VP9" ;; # VP9 was detected as rawyuy2 by older MPlayer |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# HEVC and VP9 weren't supported by older versions MPlayer |
# Seen: |
# "hevc (Main) (HEVC / 0x43564548)" in MPEG2-TS |
# "hevc" in h.265 ES |
# TODO: Enforce a minimum version of mplayer |
hevc|hevc\ *) mpid="vcs_hevc" ;; |
vp9) mpid="vcs_vp9" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# and same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
# If the decoder can't seek there's no point in continuing |
if [[ ( ( $DECODER -eq $DEC_FFMPEG ) && ( -z $fflen ) ) || |
( ( $DECODER -eq $DEC_MPLAYER ) && ( -z $mplen ) ) ]]; |
then |
warn "$CAPTURER didn't report a length, seeking won't be possible." |
return $RET_NOLEN |
fi |
[[ -z $fflen ]] && fflen=0 |
[[ -z $mplen ]] && mplen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || { |
# Fall back to ffmpeg's dimensions |
VID[$W]=${FFMPEG_ID[$W]} |
VID[$H]=${FFMPEG_ID[$H]} |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
# MPlayer tends to identify as raw video if it doesn't support the codec |
# fall back to FFmpeg in such case |
if [[ ${MPLAYER_ID[$VCODEC]} = "0x00000000" ]]; then |
VID[$VCODEC]=${FFMPEG_ID[$VCODEC]} |
VID[$VCNAME]=${FFMPEG_ID[$VCNAME]} |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
warn " Does $CAPTURER support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
local ret=$? |
VID=( "${RESULT[@]}" ) |
return $ret |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
local mpplen= |
[[ -z "${MPLAYER_ID[${LEN}]}" ]] || mpplen=$(pretty_stamp ${MPLAYER_ID[$LEN]}) |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $mpplen |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
local clen= |
[[ -z ${VID[$LEN]} ]] || clen=$(pretty_stamp ${VID[$LEN]}) |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $clen |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $NUM_COLUMNS ]]; then |
numcols=$n |
else |
numcols=$NUM_COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || { |
exitcode=$? |
[[ ${#capfiles[@]} -gt 0 ]] || { |
# No successful capture, unsupported format? |
# TODO: Adapt to capturer in use |
warn "No successful capture, possible unsupported format." |
} |
return $exitcode |
} |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $NUM_COLUMNS ]]; then |
numcols=$n |
else |
numcols=$NUM_COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'NUM_COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $NUM_COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
# File size rounding |
"get_pretty_size 1127428915 1.05%20GiB #Leading zeroes in file size (GiB)" |
"get_pretty_size 1132462 1.08%20MiB #Leading zeroes in file size (MiB)" |
"get_pretty_size 1116 1.09%20KiB #Leading zeroes in file size (KiB)" |
"get_pretty_size 1889785610 1.76%20GiB #Pretty-printed file size (GiB)" |
"get_pretty_size 762650296 727.32%20MiB #Pretty-printed file size (MiB)" |
"get_pretty_size 524810 512.51%20KiB #Pretty-printed file size (KiB)" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
val=${val/\%20/ } |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Expected '$val'. Got '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2019 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
NOTE: If you have any configuration loaded before this |
takes effect the script might still print some |
colour. You can disable it completely by setting |
the TERM variable to a monochrome term type, e.g.: |
$ env TERM=vt100 vcs [options] |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
NUM_COLUMNS="$2" |
USR_NUM_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
if type -paf lsb_release >/dev/null ; then |
lsb_release=$(lsb_release -d | cut -d: -f2- | sed 's/^[[:space:]]*//') |
fi |
imversion=$(convert --version | head -1 | cut -d' ' -f2-) |
if [[ -n "$MPLAYER_BIN" ]]; then |
mpversion=$("$MPLAYER_BIN" --version 2>/dev/null || true) |
# Older mplayer doesn't understand --version... |
if grep "Unknown option" <<<"$mpversion" ; then |
# ...But the last output line contains the version in my sample |
mpversion=$(tail -1 <<<"$mpversion") |
fi |
fi |
if [[ -n "$FFMPEG_BIN" ]]; then |
# Older versions print to stderr, newer to stdout |
ffversion=$("$FFMPEG_BIN" -version 2>&1 | head -1) |
lavcversion=$("$FFMPEG_BIN" -version 2>&1 | grep libavcodec \ |
| sed 's/[[:space:]][[:space:]]*/ /') |
ffversion="$ffversion / $lavcversion" |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
MPlayer: $mpversion |
FFMpeg: $ffversion |
ImageMagick: $imversion |
LSB Description: $lsb_release |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/rpm/rpmlint.conf |
---|
0,0 → 1,2 |
addFilter("E:.* no-signature") |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/rpm/references |
---|
0,0 → 1,21 |
Some useful references: |
"Creating RPM Packages with Fedora" |
<https://fedoraproject.org/wiki/How_to_create_an_RPM_package> |
"Packaging for openSUSE Leap" |
<https://en.opensuse.org/openSUSE:How_to_contribute_to_Leap> |
"Build Service cross distribution howto" |
<https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto> |
"Fedora packaging guidelines" |
<https://fedoraproject.org/wiki/Packaging:Guidelines> |
Alternative requirements: |
As of 2017 there's some conflicting information on boolean operators |
[1] says they are not supported in Requires |
[2] says they are supported in all fields, including requires (rpm >= 4.13) |
1: <https://fedoraproject.org/wiki/Packaging:Guidelines#Rich.2FBoolean_dependencies> |
2: <http://rpm.org/user_doc/boolean_dependencies.html> |
Fedora 25 has RPM 4.13 |
openSUSE Leap 42.2 has RPM 4.11 |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/rpm/vcs.spec.in |
---|
0,0 → 1,124 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# originally based on mp3plot's which in turn was based on other sources |
%define is_suse 0%{?suse_version} |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPLv2+ |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
#Requires: rpm >= 4.13 |
#Requires: ( mplayer or ffmpeg ) |
Requires: ffmpeg |
Recommends: mplayer |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
# as per rpmlint: E: wrong-script-interpreter /usr/bin/vcs /usr/bin/env bash |
sed -i '1s@.*@#!/bin/bash@' %buildroot/usr/bin/vcs |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat May 20 2017 - outlyer (at) gmail (dot) com |
- Rewrote dependencies with notes about boolean (alternative) dependecies |
- Depend on ffmpeg and recommend mplayer while distributions catch up with RPM |
- Removed Mandrake detection and updated the macro for SUSE |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/common.mk |
---|
0,0 → 1,101 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
# handles .tar.gz, .tar.bz2 and .tar.xz |
vcs-$(VERSION).tar: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar cf $@ vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
vcs-$(VERSION).tar.gz: vcs-$(VERSION).tar |
gzip -9 $< |
vcs-$(VERSION).tar.bz2: vcs-$(VERSION).tar |
bzip2 -9 $< |
vcs-$(VERSION).tar.xz: vcs-$(VERSION).tar |
xz -9 $< |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/docs/src/plain_messages_note.man.inc.xml |
---|
0,0 → 1,16 |
<!DOCTYPE para PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
<!-- This file is meant to be included, it contains the explanation |
about handling of colourised (console) output, to be shared |
by both the documentation of -Wc and plain_messages. --> |
<para> |
<note> |
<para>Some colour will be printed by default until this option is handled. |
If you need to completely disable colour, e.g. to run in cron jobs, you can |
do so by setting an appropriate TERM environment variable e.g. |
</para> |
<!-- Note: literallayout allows injection of linebreaks (Docbook has no <br /> but HTML output doesn't come out too pretty--> |
<para><literal>$ <command>TERM=<replaceable>vt100</replaceable> vcs</command></literal></para> |
<para>will make the script switch to monochrome output altogether.</para> |
</note> |
</para> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,594 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date$ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,1085 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5.1: The Definitive Guide (includes list of elements) |
<http://tdg.docbook.org/tdg/5.1/> |
- ": refentry - A reference page (originally a UNIX man-style reference page). |
<http://tdg.docbook.org/tdg/5.1/refentry.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man ./vcs.1 | less |
or |
$ man ./vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). |
"Video Contact Sheet *NIX User Manual" is too long and |
"Manual" is cropped off --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
<!-- for vcs.conf(5) --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY confpackage "vcs.conf"> |
<!ENTITY confsection "5"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!-- END OF DOCTYPE --> |
<reference><!-- Group of refentry's --> |
<!-- This is required by reference to validate with e.g. xmlto. |
NOTE This appears to override the title set below. --> |
<title>&title;</title> |
<!-- START OF man(1) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<date>Last revision: 2017-05-23</date><!-- Exported on: $Date$ --> |
<revhistory> |
<revision> |
<date>2017-05-23</date> |
<revremark>Added note about disabling colour output (starting with 1.13.3).</revremark> |
</revision> |
<revision> |
<date>2011-08-29</date> |
</revision> |
</revhistory> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="option_wc"><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
</listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><envar>TERM</envar></term> |
<listitem> |
<para>Affects the usage of colour output to console being on or off |
by default. See the documentation for <option><xref linkend="option_wc" /></option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- END OF vcs(1) --> |
<!-- START OF vcs.conf(5) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&confpackage;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
<date>Last revision: 2011-08-29</date> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&confpackage;</refentrytitle> |
<manvolnum>&confsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&confpackage;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<!-- Note: \[char46] is a escaped dot for groff. |
Otherwise this paragraph's output will start a line |
with a dot, which makes groff/man interpret it as an |
inexistent macro, |
i.e. the filename won't render at all |
Reference: http://stackoverflow.com/questions/11469341/ |
--> |
<para><filename>\[char46]/vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- END OF vcs.conf(5) --> |
</reference> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/docs/GNUmakefile |
---|
0,0 → 1,134 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
# Since 1.13.3 the man pages are combined into a single input |
# The XHTML output contains both man pages as a side effect, while the groff |
# and PDF output are separate for each man page. TeX output was removed from |
# this makefile. |
DEFAULT=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .pdf) \ |
$(addprefix vcs.conf.man,.pdf) \ |
) |
EXTRA=$(addprefix $(distdir)/,vcs.man2html.html vcs.conf.man2html.html) |
ALL=$(DEFAULT) $(EXTRA) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_COMMON=xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" |
# NOT: For manpages the output can be a directory, whereas for XHTML it can not |
DOCBOOK_TO_MAN=$(DOCBOOK_TO_COMMON) -o $(distdir)/ $(DOCBOOK_XSL)/manpages/docbook.xsl |
DOCBOOK_TO_XHTML=$(DOCBOOK_TO_COMMON) $(DOCBOOK_XSL)/xhtml/docbook.xsl |
default: $(DEFAULT) |
extra: $(EXTRA) |
all: $(ALL) |
env: |
@echo --- Values of Makefile variables: --- |
@echo DEFAULT: $(DEFAULT) |
@echo EXTRA: $(EXTRA) |
@echo ALL: $(ALL) |
@echo INTERMEDIATE: $(INTERMEDIATE) |
@echo DOCBOOK_XSL: $(DOCBOOK_XSL) |
@echo DOCBOOK_TO_MAN: $$ $(DOCBOOK_TO_MAN) |
@echo DOCBOOK_TO_XHTML: $$ $(DOCBOOK_TO_XHTML) |
@echo distdir: $(distdir) |
@echo srcdir: $(srcdir) |
@echo ------------------------------------- |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# These are both generated at once |
$(distdir)/vcs.1 $(distdir)/vcs.conf.5: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# Note with both manpages combined the output is combined too |
$(distdir)/vcs.man.html: $(srcdir)/vcs.man.xml |
$(DOCBOOK_TO_XHTML) "$<" > "$@" || ( $(RM) "$@" && false ) |
@# sed post processing: |
@# add CSS link |
@# obfuscate mailto: links |
@# obfuscate emails |
@# replace groff-escaped dots ("\[char46]") |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
-e 's/\\\[char46\]/./g' \ |
"$@" |
@# man2html includes the last revision date, which xsltproc does not, it |
@# seems useful to me, so I'm injecting it here |
sed -i \ |
-e 's!</h1>!</h1>$(shell grep 'Last revision' $< | head -1 \ |
| sed -e 's!.*<date>!!' \ |
-e 's!</date>.*$$!!')!' \ |
"$@" |
##### |
##### RULES SHARING RECIPES |
##### See http://stackoverflow.com/questions/11441084/makefile-with-multiples-rules-sharing-same-recipe |
##### |
# 1/2: Pre-requisites |
$(distdir)/vcs.conf.man.pdf: $(distdir)/vcs.conf.5.pdf |
$(distdir)/vcs.man.pdf: $(distdir)/vcs.1.pdf |
$(distdir)/vcs.man2html.html: $(distdir)/vcs.1 |
$(distdir)/vcs.conf.man2html.html: $(distdir)/vcs.conf.5 |
# 2/2: Common recipe |
$(distdir)/vcs.conf.man.pdf $(distdir)/vcs.man.pdf: |
mv $< $@ |
##### |
##### / END OF RULES SHARING RECIPES |
##### |
$(distdir)/vcs.man2html.html $(distdir)/vcs.conf.man2html.html: |
@# The first two lines of man2html are HTTP headers |
@# The manpage is preprocessed to replace groff-escaped dots (\[char46]) |
sed -e 's/\\\[char46\]/\\./g' "$<" | man2html -r | sed 1,2d > "$@" |
# jade and docbook-to-man conversions don't appear to agree on what's the |
# correct structure, to avoid this here I use doclifter to convert back from |
# man to DocBook, which generates a less semantically-rich but easier to |
# process DocBook file |
$(distdir)/vcs.%.pdf: vcs.% |
doclifter < $< > temp.xml || ( $(RM) temp.xml && false ) |
db2pdf temp.xml || ( $(RM) temp.xml && false ) |
-$(RM) temp.xml |
mv temp.pdf $@ |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.html' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.html, $(DEFAULT)) |
.PHONY: all default extra env clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/arch/PKGBUILD.in |
---|
0,0 → 1,43 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('mplayer: for better more complete detection/capture' |
'lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.4/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/GNUmakefile |
---|
0,0 → 1,142 |
#!/usr/bin/make -f |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.%: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.%: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n 'Ignore? [y/N] ' ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
@if ! sed -e '/$$Date/!d' dist/vcs | grep -E 'Mon|Tue|Wed|Thu|Fri|Sat|Sun' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from localised SVN export **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n "Ignore? [y/N] " ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
false ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .xz .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb srpm |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).xz: $(srcdir)/vcs |
xz -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint --file=dist/rpm/rpmlint.conf vcs-$(VER)-*.rpm |
srpm: vcs-$(VER).tar.gz |
rpmbuild --clean -ts vcs-$(VER).tar.gz |
test -d ~/rpmbuild/SRPMS && ln -s ~/rpmbuild/SRPMS/vcs-$(VER)-*.src.rpm . || true |
test -d ~/RPM/SRPMS && ln -s ~/RPM/SRPMS/vcs-$(VER)-*.src.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint vcs-$(VER)-*.src.rpm |
false |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/online_man/Makefile |
---|
0,0 → 1,18 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html |
man.vcs.html: $(docsdir)/vcs.man.html |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
.PHONY: all clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.4/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.4/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/branches/1.13.2-pre.3+early_color:r664-665 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/tags/1.13.2-pre.4:r666 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/CHANGELOG |
---|
0,0 → 1,529 |
1.13.4 (?): |
* BUGFIX: Actually use all alternative evasion offsets (Bugfix by Davide |
Cavestro) [#364] |
* BUGFIX: Display file sizes correctly when using mawk [#365] |
* BUGFIX: Number of columns ignored on Bash 5.0 [#373] |
1.13.3 (2017-05-26): |
* Added codec IDs for h.265 and VP9 |
* BUGFIX: Fix handling of failed captures |
* BUGFIX: Fix handling of failed identification |
* BUGFIX: Cleaned output for identification of unsupported file types |
* BUGFIX: Codec information was getting cropped with current versions of |
ImageMagick. Gravity appears to be interpreted in a different way |
now. (Bugfix by Markus) [#323] |
* BUGFIX: Fix incorrect calculation of file size in header. (Based on an |
anonymous patch) [#314] |
* OTHER: Print warning about possible lack of support if no frame could be |
captured |
* OTHER: Don't trust MPlayer's detection of raw video, use FFmpeg's |
detection in such case |
* OTHER: Fix incorrect rendering of Note #1 in vcs.conf's manpage |
* OTHER: Clean up generation and conversion of manpages |
* OTHER: Added versions of MPlayer, FFMpeg, ImageMagick and LSB to debug |
output |
* OTHER: Allow disabling coloured output altogether. [#311] |
This is implemented by honouring $TERM, e.g. "TERM=vt100 vcs ..." |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/vcs |
---|
0,0 → 1,5348 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007-2019 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.4" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=2 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
# Starting with Bash 5 uppercase $COLUMNS can't be safely set in the script. |
declare -i PADDING=2 |
declare -i NUM_COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"NUM_COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"COLUMNS:NUM_COLUMNS:alias:p" # Up to 1.13.3 |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
# Sizes are always rounded up (hence the addition 0.999999 to the fractionary part) |
# gawk understands the ** operator, but mawk does not, using precomputed |
# values for the sake of compatibility |
declare -ri GBS=$(( 1024**3 )) |
declare -ri MBS=$(( 1024**2 )) |
if [[ $bytes -gt $GBS ]]; then |
local gibs_int=$(( $bytes / $GBS )) |
local gibs_frac=$(awkex "int($bytes%$GBS*100/$GBS + 0.999999)" ) |
size="$(printf '%d.%02d' $gibs_int $gibs_frac) GiB" |
elif [[ $bytes -gt $MBS ]]; then |
local mibs_int=$(( $bytes / $MBS )) |
local mibs_frac=$(awkex "int($bytes%$MBS*100/$MBS + 0.999999)") |
size="$(printf '%d.%02d' $mibs_int $mibs_frac) MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs_int=$(( $bytes / 1024 )) |
local kibs_frac=$(awkex "int($bytes%1024*100/1024 + 0.999999)") |
size="$(printf '%d.%02d' $kibs_int $kibs_frac) KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# First we must find the correct way to query color support. |
# There's basically two variants of tput: |
# terminfo (Linux) and termcap (FreeBSD) |
# These is an issue for portability: |
# - On Linux 'tput colors' is used to query it |
# - On FreeBSD 'tput Co' is used to query it |
# - Linux's tput will fail if it's passed 'Co' |
# - FreeBSD's tput will interpret 'colors' as 'co' and print the number of columns |
local tputc="-1" |
if tput Co >/dev/null 2>&1 ; then |
tputc=$(tput Co) # termcap style |
else |
# Try to guess if it's parsing it as columns |
# The method here is to check against some known terminals |
# pilot: 39 columns mono, pc3: 80 columns, 8 colors |
if [[ 8 = "$(tput -T pc3 colors)" ]]; then |
# colors is interpreted literally |
tputc=$(tput colors) |
fi |
fi |
# Is it able to set colours? |
# Linux's tput can be passed arguments to retrieve the correct escape sequences |
# FreeBSD's tput can not |
if tput bold && [[ "-1" != "$tputc" ]] && tput setaf 0 && tput sgr0; then |
# Can configure completely through tput |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
elif [[ "-1" != "$tputc" ]]; then |
# tput reports color support but it doesn't provide |
# the escape codes directly, will use hardcoded escape codes instead |
HAS_COLORS= |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# NOTE: These messages are supressed if called from classic_identify |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in ${EVASION_ALTERNATIVES[@]} ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
RESULT= |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} || { |
# Failed capture |
return $? |
} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)." |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
# Allow both the synthetic (for older mplayer) and builtin (for newer mplayer) codec ids |
vcs_hevc|HEVC) vcodec="HEVC" ;; |
vcs_vp9|VP90) vcodec="VP9" ;; # VP9 was detected as rawyuy2 by older MPlayer |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# HEVC and VP9 weren't supported by older versions MPlayer |
# Seen: |
# "hevc (Main) (HEVC / 0x43564548)" in MPEG2-TS |
# "hevc" in h.265 ES |
# TODO: Enforce a minimum version of mplayer |
hevc|hevc\ *) mpid="vcs_hevc" ;; |
vp9) mpid="vcs_vp9" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# and same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
# If the decoder can't seek there's no point in continuing |
if [[ ( ( $DECODER -eq $DEC_FFMPEG ) && ( -z $fflen ) ) || |
( ( $DECODER -eq $DEC_MPLAYER ) && ( -z $mplen ) ) ]]; |
then |
warn "$CAPTURER didn't report a length, seeking won't be possible." |
return $RET_NOLEN |
fi |
[[ -z $fflen ]] && fflen=0 |
[[ -z $mplen ]] && mplen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || { |
# Fall back to ffmpeg's dimensions |
VID[$W]=${FFMPEG_ID[$W]} |
VID[$H]=${FFMPEG_ID[$H]} |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
# MPlayer tends to identify as raw video if it doesn't support the codec |
# fall back to FFmpeg in such case |
if [[ ${MPLAYER_ID[$VCODEC]} = "0x00000000" ]]; then |
VID[$VCODEC]=${FFMPEG_ID[$VCODEC]} |
VID[$VCNAME]=${FFMPEG_ID[$VCNAME]} |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
warn " Does $CAPTURER support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
local ret=$? |
VID=( "${RESULT[@]}" ) |
return $ret |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
local mpplen= |
[[ -z "${MPLAYER_ID[${LEN}]}" ]] || mpplen=$(pretty_stamp ${MPLAYER_ID[$LEN]}) |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $mpplen |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
local clen= |
[[ -z ${VID[$LEN]} ]] || clen=$(pretty_stamp ${VID[$LEN]}) |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $clen |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $NUM_COLUMNS ]]; then |
numcols=$n |
else |
numcols=$NUM_COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || { |
exitcode=$? |
[[ ${#capfiles[@]} -gt 0 ]] || { |
# No successful capture, unsupported format? |
# TODO: Adapt to capturer in use |
warn "No successful capture, possible unsupported format." |
} |
return $exitcode |
} |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $NUM_COLUMNS ]]; then |
numcols=$n |
else |
numcols=$NUM_COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'NUM_COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $NUM_COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
# File size rounding |
"get_pretty_size 1127428915 1.05%20GiB #Leading zeroes in file size (GiB)" |
"get_pretty_size 1132462 1.08%20MiB #Leading zeroes in file size (MiB)" |
"get_pretty_size 1116 1.09%20KiB #Leading zeroes in file size (KiB)" |
"get_pretty_size 1889785610 1.76%20GiB #Pretty-printed file size (GiB)" |
"get_pretty_size 762650296 727.32%20MiB #Pretty-printed file size (MiB)" |
"get_pretty_size 524810 512.51%20KiB #Pretty-printed file size (KiB)" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
val=${val/\%20/ } |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Expected '$val'. Got '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2019 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
NOTE: If you have any configuration loaded before this |
takes effect the script might still print some |
colour. You can disable it completely by setting |
the TERM variable to a monochrome term type, e.g.: |
$ env TERM=vt100 vcs [options] |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
NUM_COLUMNS="$2" |
USR_NUM_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
if type -paf lsb_release >/dev/null ; then |
lsb_release=$(lsb_release -d | cut -d: -f2- | sed 's/^[[:space:]]*//') |
fi |
imversion=$(convert --version | head -1 | cut -d' ' -f2-) |
if [[ -n "$MPLAYER_BIN" ]]; then |
mpversion=$("$MPLAYER_BIN" --version 2>/dev/null || true) |
# Older mplayer doesn't understand --version... |
if grep "Unknown option" <<<"$mpversion" ; then |
# ...But the last output line contains the version in my sample |
mpversion=$(tail -1 <<<"$mpversion") |
fi |
fi |
if [[ -n "$FFMPEG_BIN" ]]; then |
# Older versions print to stderr, newer to stdout |
ffversion=$("$FFMPEG_BIN" -version 2>&1 | head -1) |
lavcversion=$("$FFMPEG_BIN" -version 2>&1 | grep libavcodec \ |
| sed 's/[[:space:]][[:space:]]*/ /') |
ffversion="$ffversion / $lavcversion" |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
MPlayer: $mpversion |
FFMpeg: $ffversion |
ImageMagick: $imversion |
LSB Description: $lsb_release |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/source/format |
---|
0,0 → 1,0 |
3.0 (quilt) |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/changelog |
---|
0,0 → 1,111 |
vcs (1.13.3-pon.1) experimental; urgency=medium |
* New version |
* debian/control: Added xsltproc to Build-Depends |
* debian/compat: Bumped compatibility level to 9 (jessie) |
* debian/control: Bumped build-dependancy on debhelper to >= 9 |
(compatibility level 9) |
-- Toni Corvera <outlyer@gmail.com> Sat, 20 May 2017 00:04:51 +0200 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/compat |
---|
0,0 → 1,0 |
9 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 9), xsltproc |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/common.mk |
---|
0,0 → 1,101 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
# handles .tar.gz, .tar.bz2 and .tar.xz |
vcs-$(VERSION).tar: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar cf $@ vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
vcs-$(VERSION).tar.gz: vcs-$(VERSION).tar |
gzip -9 $< |
vcs-$(VERSION).tar.bz2: vcs-$(VERSION).tar |
bzip2 -9 $< |
vcs-$(VERSION).tar.xz: vcs-$(VERSION).tar |
xz -9 $< |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/docs/src/plain_messages_note.man.inc.xml |
---|
0,0 → 1,16 |
<!DOCTYPE para PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
<!-- This file is meant to be included, it contains the explanation |
about handling of colourised (console) output, to be shared |
by both the documentation of -Wc and plain_messages. --> |
<para> |
<note> |
<para>Some colour will be printed by default until this option is handled. |
If you need to completely disable colour, e.g. to run in cron jobs, you can |
do so by setting an appropriate TERM environment variable e.g. |
</para> |
<!-- Note: literallayout allows injection of linebreaks (Docbook has no <br /> but HTML output doesn't come out too pretty--> |
<para><literal>$ <command>TERM=<replaceable>vt100</replaceable> vcs</command></literal></para> |
<para>will make the script switch to monochrome output altogether.</para> |
</note> |
</para> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,594 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date$ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,1085 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5.1: The Definitive Guide (includes list of elements) |
<http://tdg.docbook.org/tdg/5.1/> |
- ": refentry - A reference page (originally a UNIX man-style reference page). |
<http://tdg.docbook.org/tdg/5.1/refentry.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man ./vcs.1 | less |
or |
$ man ./vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). |
"Video Contact Sheet *NIX User Manual" is too long and |
"Manual" is cropped off --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
<!-- for vcs.conf(5) --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY confpackage "vcs.conf"> |
<!ENTITY confsection "5"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!-- END OF DOCTYPE --> |
<reference><!-- Group of refentry's --> |
<!-- This is required by reference to validate with e.g. xmlto. |
NOTE This appears to override the title set below. --> |
<title>&title;</title> |
<!-- START OF man(1) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<date>Last revision: 2017-05-23</date><!-- Exported on: $Date$ --> |
<revhistory> |
<revision> |
<date>2017-05-23</date> |
<revremark>Added note about disabling colour output (starting with 1.13.3).</revremark> |
</revision> |
<revision> |
<date>2011-08-29</date> |
</revision> |
</revhistory> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="option_wc"><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
</listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><envar>TERM</envar></term> |
<listitem> |
<para>Affects the usage of colour output to console being on or off |
by default. See the documentation for <option><xref linkend="option_wc" /></option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- END OF vcs(1) --> |
<!-- START OF vcs.conf(5) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&confpackage;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
<date>Last revision: 2011-08-29</date> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&confpackage;</refentrytitle> |
<manvolnum>&confsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&confpackage;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<!-- Note: \[char46] is a escaped dot for groff. |
Otherwise this paragraph's output will start a line |
with a dot, which makes groff/man interpret it as an |
inexistent macro, |
i.e. the filename won't render at all |
Reference: http://stackoverflow.com/questions/11469341/ |
--> |
<para><filename>\[char46]/vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- END OF vcs.conf(5) --> |
</reference> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/docs/GNUmakefile |
---|
0,0 → 1,134 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
# Since 1.13.3 the man pages are combined into a single input |
# The XHTML output contains both man pages as a side effect, while the groff |
# and PDF output are separate for each man page. TeX output was removed from |
# this makefile. |
DEFAULT=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .pdf) \ |
$(addprefix vcs.conf.man,.pdf) \ |
) |
EXTRA=$(addprefix $(distdir)/,vcs.man2html.html vcs.conf.man2html.html) |
ALL=$(DEFAULT) $(EXTRA) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_COMMON=xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" |
# NOT: For manpages the output can be a directory, whereas for XHTML it can not |
DOCBOOK_TO_MAN=$(DOCBOOK_TO_COMMON) -o $(distdir)/ $(DOCBOOK_XSL)/manpages/docbook.xsl |
DOCBOOK_TO_XHTML=$(DOCBOOK_TO_COMMON) $(DOCBOOK_XSL)/xhtml/docbook.xsl |
default: $(DEFAULT) |
extra: $(EXTRA) |
all: $(ALL) |
env: |
@echo --- Values of Makefile variables: --- |
@echo DEFAULT: $(DEFAULT) |
@echo EXTRA: $(EXTRA) |
@echo ALL: $(ALL) |
@echo INTERMEDIATE: $(INTERMEDIATE) |
@echo DOCBOOK_XSL: $(DOCBOOK_XSL) |
@echo DOCBOOK_TO_MAN: $$ $(DOCBOOK_TO_MAN) |
@echo DOCBOOK_TO_XHTML: $$ $(DOCBOOK_TO_XHTML) |
@echo distdir: $(distdir) |
@echo srcdir: $(srcdir) |
@echo ------------------------------------- |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# These are both generated at once |
$(distdir)/vcs.1 $(distdir)/vcs.conf.5: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# Note with both manpages combined the output is combined too |
$(distdir)/vcs.man.html: $(srcdir)/vcs.man.xml |
$(DOCBOOK_TO_XHTML) "$<" > "$@" || ( $(RM) "$@" && false ) |
@# sed post processing: |
@# add CSS link |
@# obfuscate mailto: links |
@# obfuscate emails |
@# replace groff-escaped dots ("\[char46]") |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
-e 's/\\\[char46\]/./g' \ |
"$@" |
@# man2html includes the last revision date, which xsltproc does not, it |
@# seems useful to me, so I'm injecting it here |
sed -i \ |
-e 's!</h1>!</h1>$(shell grep 'Last revision' $< | head -1 \ |
| sed -e 's!.*<date>!!' \ |
-e 's!</date>.*$$!!')!' \ |
"$@" |
##### |
##### RULES SHARING RECIPES |
##### See http://stackoverflow.com/questions/11441084/makefile-with-multiples-rules-sharing-same-recipe |
##### |
# 1/2: Pre-requisites |
$(distdir)/vcs.conf.man.pdf: $(distdir)/vcs.conf.5.pdf |
$(distdir)/vcs.man.pdf: $(distdir)/vcs.1.pdf |
$(distdir)/vcs.man2html.html: $(distdir)/vcs.1 |
$(distdir)/vcs.conf.man2html.html: $(distdir)/vcs.conf.5 |
# 2/2: Common recipe |
$(distdir)/vcs.conf.man.pdf $(distdir)/vcs.man.pdf: |
mv $< $@ |
##### |
##### / END OF RULES SHARING RECIPES |
##### |
$(distdir)/vcs.man2html.html $(distdir)/vcs.conf.man2html.html: |
@# The first two lines of man2html are HTTP headers |
@# The manpage is preprocessed to replace groff-escaped dots (\[char46]) |
sed -e 's/\\\[char46\]/\\./g' "$<" | man2html -r | sed 1,2d > "$@" |
# jade and docbook-to-man conversions don't appear to agree on what's the |
# correct structure, to avoid this here I use doclifter to convert back from |
# man to DocBook, which generates a less semantically-rich but easier to |
# process DocBook file |
$(distdir)/vcs.%.pdf: vcs.% |
doclifter < $< > temp.xml || ( $(RM) temp.xml && false ) |
db2pdf temp.xml || ( $(RM) temp.xml && false ) |
-$(RM) temp.xml |
mv temp.pdf $@ |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.html' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.html, $(DEFAULT)) |
.PHONY: all default extra env clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/rpm/references |
---|
0,0 → 1,21 |
Some useful references: |
"Creating RPM Packages with Fedora" |
<https://fedoraproject.org/wiki/How_to_create_an_RPM_package> |
"Packaging for openSUSE Leap" |
<https://en.opensuse.org/openSUSE:How_to_contribute_to_Leap> |
"Build Service cross distribution howto" |
<https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto> |
"Fedora packaging guidelines" |
<https://fedoraproject.org/wiki/Packaging:Guidelines> |
Alternative requirements: |
As of 2017 there's some conflicting information on boolean operators |
[1] says they are not supported in Requires |
[2] says they are supported in all fields, including requires (rpm >= 4.13) |
1: <https://fedoraproject.org/wiki/Packaging:Guidelines#Rich.2FBoolean_dependencies> |
2: <http://rpm.org/user_doc/boolean_dependencies.html> |
Fedora 25 has RPM 4.13 |
openSUSE Leap 42.2 has RPM 4.11 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/rpm/vcs.spec.in |
---|
0,0 → 1,124 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# originally based on mp3plot's which in turn was based on other sources |
%define is_suse 0%{?suse_version} |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPLv2+ |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
#Requires: rpm >= 4.13 |
#Requires: ( mplayer or ffmpeg ) |
Requires: ffmpeg |
Recommends: mplayer |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
# as per rpmlint: E: wrong-script-interpreter /usr/bin/vcs /usr/bin/env bash |
sed -i '1s@.*@#!/bin/bash@' %buildroot/usr/bin/vcs |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat May 20 2017 - outlyer (at) gmail (dot) com |
- Rewrote dependencies with notes about boolean (alternative) dependecies |
- Depend on ffmpeg and recommend mplayer while distributions catch up with RPM |
- Removed Mandrake detection and updated the macro for SUSE |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/arch/PKGBUILD.in |
---|
0,0 → 1,43 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('mplayer: for better more complete detection/capture' |
'lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/GNUmakefile |
---|
0,0 → 1,142 |
#!/usr/bin/make -f |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.%: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.%: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n 'Ignore? [y/N] ' ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
@if ! sed -e '/$$Date/!d' dist/vcs | grep -E 'Mon|Tue|Wed|Thu|Fri|Sat|Sun' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from localised SVN export **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n "Ignore? [y/N] " ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
false ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .xz .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb srpm |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).xz: $(srcdir)/vcs |
xz -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint vcs-$(VER)-*.rpm |
srpm: vcs-$(VER).tar.gz |
rpmbuild --clean -ts vcs-$(VER).tar.gz |
test -d ~/rpmbuild/SRPMS && ln -s ~/rpmbuild/SRPMS/vcs-$(VER)-*.src.rpm . || true |
test -d ~/RPM/SRPMS && ln -s ~/RPM/SRPMS/vcs-$(VER)-*.src.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint vcs-$(VER)-*.src.rpm |
false |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/online_man/Makefile |
---|
0,0 → 1,18 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html |
man.vcs.html: $(docsdir)/vcs.man.html |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
.PHONY: all clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/branches/1.13.2-pre.3+early_color:r664-665 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/tags/1.13.2-pre.4:r666 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/vcs |
---|
0,0 → 1,5346 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007-2019 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.4" |
declare -r RELEASE=1 |
declare -ri PRERELEASE=1 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
# Sizes are always rounded up (hence the addition 0.999999 to the fractionary part) |
# gawk understands the ** operator, but mawk does not, using precomputed |
# values for the sake of compatibility |
declare -ri GBS=$(( 1024**3 )) |
declare -ri MBS=$(( 1024**2 )) |
if [[ $bytes -gt $GBS ]]; then |
local gibs_int=$(( $bytes / $GBS )) |
local gibs_frac=$(awkex "int($bytes%$GBS*100/$GBS + 0.999999)" ) |
size="$(printf '%d.%02d' $gibs_int $gibs_frac) GiB" |
elif [[ $bytes -gt $MBS ]]; then |
local mibs_int=$(( $bytes / $MBS )) |
local mibs_frac=$(awkex "int($bytes%$MBS*100/$MBS + 0.999999)") |
size="$(printf '%d.%02d' $mibs_int $mibs_frac) MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs_int=$(( $bytes / 1024 )) |
local kibs_frac=$(awkex "int($bytes%1024*100/1024 + 0.999999)") |
size="$(printf '%d.%02d' $kibs_int $kibs_frac) KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# First we must find the correct way to query color support. |
# There's basically two variants of tput: |
# terminfo (Linux) and termcap (FreeBSD) |
# These is an issue for portability: |
# - On Linux 'tput colors' is used to query it |
# - On FreeBSD 'tput Co' is used to query it |
# - Linux's tput will fail if it's passed 'Co' |
# - FreeBSD's tput will interpret 'colors' as 'co' and print the number of columns |
local tputc="-1" |
if tput Co >/dev/null 2>&1 ; then |
tputc=$(tput Co) # termcap style |
else |
# Try to guess if it's parsing it as columns |
# The method here is to check against some known terminals |
# pilot: 39 columns mono, pc3: 80 columns, 8 colors |
if [[ 8 = "$(tput -T pc3 colors)" ]]; then |
# colors is interpreted literally |
tputc=$(tput colors) |
fi |
fi |
# Is it able to set colours? |
# Linux's tput can be passed arguments to retrieve the correct escape sequences |
# FreeBSD's tput can not |
if tput bold && [[ "-1" != "$tputc" ]] && tput setaf 0 && tput sgr0; then |
# Can configure completely through tput |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
elif [[ "-1" != "$tputc" ]]; then |
# tput reports color support but it doesn't provide |
# the escape codes directly, will use hardcoded escape codes instead |
HAS_COLORS= |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# NOTE: These messages are supressed if called from classic_identify |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in ${EVASION_ALTERNATIVES[@]} ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
RESULT= |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} || { |
# Failed capture |
return $? |
} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)." |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
# Allow both the synthetic (for older mplayer) and builtin (for newer mplayer) codec ids |
vcs_hevc|HEVC) vcodec="HEVC" ;; |
vcs_vp9|VP90) vcodec="VP9" ;; # VP9 was detected as rawyuy2 by older MPlayer |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# HEVC and VP9 weren't supported by older versions MPlayer |
# Seen: |
# "hevc (Main) (HEVC / 0x43564548)" in MPEG2-TS |
# "hevc" in h.265 ES |
# TODO: Enforce a minimum version of mplayer |
hevc|hevc\ *) mpid="vcs_hevc" ;; |
vp9) mpid="vcs_vp9" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# and same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
# If the decoder can't seek there's no point in continuing |
if [[ ( ( $DECODER -eq $DEC_FFMPEG ) && ( -z $fflen ) ) || |
( ( $DECODER -eq $DEC_MPLAYER ) && ( -z $mplen ) ) ]]; |
then |
warn "$CAPTURER didn't report a length, seeking won't be possible." |
return $RET_NOLEN |
fi |
[[ -z $fflen ]] && fflen=0 |
[[ -z $mplen ]] && mplen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || { |
# Fall back to ffmpeg's dimensions |
VID[$W]=${FFMPEG_ID[$W]} |
VID[$H]=${FFMPEG_ID[$H]} |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
# MPlayer tends to identify as raw video if it doesn't support the codec |
# fall back to FFmpeg in such case |
if [[ ${MPLAYER_ID[$VCODEC]} = "0x00000000" ]]; then |
VID[$VCODEC]=${FFMPEG_ID[$VCODEC]} |
VID[$VCNAME]=${FFMPEG_ID[$VCNAME]} |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
warn " Does $CAPTURER support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
local ret=$? |
VID=( "${RESULT[@]}" ) |
return $ret |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
local mpplen= |
[[ -z "${MPLAYER_ID[${LEN}]}" ]] || mpplen=$(pretty_stamp ${MPLAYER_ID[$LEN]}) |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $mpplen |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
local clen= |
[[ -z ${VID[$LEN]} ]] || clen=$(pretty_stamp ${VID[$LEN]}) |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $clen |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || { |
exitcode=$? |
[[ ${#capfiles[@]} -gt 0 ]] || { |
# No successful capture, unsupported format? |
# TODO: Adapt to capturer in use |
warn "No successful capture, possible unsupported format." |
} |
return $exitcode |
} |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
# File size rounding |
"get_pretty_size 1127428915 1.05%20GiB #Leading zeroes in file size (GiB)" |
"get_pretty_size 1132462 1.08%20MiB #Leading zeroes in file size (MiB)" |
"get_pretty_size 1116 1.09%20KiB #Leading zeroes in file size (KiB)" |
"get_pretty_size 1889785610 1.76%20GiB #Pretty-printed file size (GiB)" |
"get_pretty_size 762650296 727.32%20MiB #Pretty-printed file size (MiB)" |
"get_pretty_size 524810 512.51%20KiB #Pretty-printed file size (KiB)" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
val=${val/\%20/ } |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Expected '$val'. Got '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2019 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
NOTE: If you have any configuration loaded before this |
takes effect the script might still print some |
colour. You can disable it completely by setting |
the TERM variable to a monochrome term type, e.g.: |
$ env TERM=vt100 vcs [options] |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
if type -paf lsb_release >/dev/null ; then |
lsb_release=$(lsb_release -d | cut -d: -f2- | sed 's/^[[:space:]]*//') |
fi |
imversion=$(convert --version | head -1 | cut -d' ' -f2-) |
if [[ -n "$MPLAYER_BIN" ]]; then |
mpversion=$("$MPLAYER_BIN" --version 2>/dev/null || true) |
# Older mplayer doesn't understand --version... |
if grep "Unknown option" <<<"$mpversion" ; then |
# ...But the last output line contains the version in my sample |
mpversion=$(tail -1 <<<"$mpversion") |
fi |
fi |
if [[ -n "$FFMPEG_BIN" ]]; then |
# Older versions print to stderr, newer to stdout |
ffversion=$("$FFMPEG_BIN" -version 2>&1 | head -1) |
lavcversion=$("$FFMPEG_BIN" -version 2>&1 | grep libavcodec \ |
| sed 's/[[:space:]][[:space:]]*/ /') |
ffversion="$ffversion / $lavcversion" |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
MPlayer: $mpversion |
FFMpeg: $ffversion |
ImageMagick: $imversion |
LSB Description: $lsb_release |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/CHANGELOG |
---|
0,0 → 1,528 |
1.13.4 (?): |
* BUGFIX: Actually use all alternative evasion offsets (Bugfix by Davide |
Cavestro) [#364] |
* BUGFIX: Display file sizes correctly when using mawk [#365] |
1.13.3 (2017-05-26): |
* Added codec IDs for h.265 and VP9 |
* BUGFIX: Fix handling of failed captures |
* BUGFIX: Fix handling of failed identification |
* BUGFIX: Cleaned output for identification of unsupported file types |
* BUGFIX: Codec information was getting cropped with current versions of |
ImageMagick. Gravity appears to be interpreted in a different way |
now. (Bugfix by Markus) [#323] |
* BUGFIX: Fix incorrect calculation of file size in header. (Based on an |
anonymous patch) [#314] |
* OTHER: Print warning about possible lack of support if no frame could be |
captured |
* OTHER: Don't trust MPlayer's detection of raw video, use FFmpeg's |
detection in such case |
* OTHER: Fix incorrect rendering of Note #1 in vcs.conf's manpage |
* OTHER: Clean up generation and conversion of manpages |
* OTHER: Added versions of MPlayer, FFMpeg, ImageMagick and LSB to debug |
output |
* OTHER: Allow disabling coloured output altogether. [#311] |
This is implemented by honouring $TERM, e.g. "TERM=vt100 vcs ..." |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/docs/src/plain_messages_note.man.inc.xml |
---|
0,0 → 1,16 |
<!DOCTYPE para PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
<!-- This file is meant to be included, it contains the explanation |
about handling of colourised (console) output, to be shared |
by both the documentation of -Wc and plain_messages. --> |
<para> |
<note> |
<para>Some colour will be printed by default until this option is handled. |
If you need to completely disable colour, e.g. to run in cron jobs, you can |
do so by setting an appropriate TERM environment variable e.g. |
</para> |
<!-- Note: literallayout allows injection of linebreaks (Docbook has no <br /> but HTML output doesn't come out too pretty--> |
<para><literal>$ <command>TERM=<replaceable>vt100</replaceable> vcs</command></literal></para> |
<para>will make the script switch to monochrome output altogether.</para> |
</note> |
</para> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,594 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date$ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,1085 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5.1: The Definitive Guide (includes list of elements) |
<http://tdg.docbook.org/tdg/5.1/> |
- ": refentry - A reference page (originally a UNIX man-style reference page). |
<http://tdg.docbook.org/tdg/5.1/refentry.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man ./vcs.1 | less |
or |
$ man ./vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). |
"Video Contact Sheet *NIX User Manual" is too long and |
"Manual" is cropped off --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
<!-- for vcs.conf(5) --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY confpackage "vcs.conf"> |
<!ENTITY confsection "5"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!-- END OF DOCTYPE --> |
<reference><!-- Group of refentry's --> |
<!-- This is required by reference to validate with e.g. xmlto. |
NOTE This appears to override the title set below. --> |
<title>&title;</title> |
<!-- START OF man(1) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<date>Last revision: 2017-05-23</date><!-- Exported on: $Date$ --> |
<revhistory> |
<revision> |
<date>2017-05-23</date> |
<revremark>Added note about disabling colour output (starting with 1.13.3).</revremark> |
</revision> |
<revision> |
<date>2011-08-29</date> |
</revision> |
</revhistory> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="option_wc"><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
</listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><envar>TERM</envar></term> |
<listitem> |
<para>Affects the usage of colour output to console being on or off |
by default. See the documentation for <option><xref linkend="option_wc" /></option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- END OF vcs(1) --> |
<!-- START OF vcs.conf(5) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&confpackage;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
<date>Last revision: 2011-08-29</date> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&confpackage;</refentrytitle> |
<manvolnum>&confsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&confpackage;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<!-- Note: \[char46] is a escaped dot for groff. |
Otherwise this paragraph's output will start a line |
with a dot, which makes groff/man interpret it as an |
inexistent macro, |
i.e. the filename won't render at all |
Reference: http://stackoverflow.com/questions/11469341/ |
--> |
<para><filename>\[char46]/vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- END OF vcs.conf(5) --> |
</reference> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/docs/GNUmakefile |
---|
0,0 → 1,134 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
# Since 1.13.3 the man pages are combined into a single input |
# The XHTML output contains both man pages as a side effect, while the groff |
# and PDF output are separate for each man page. TeX output was removed from |
# this makefile. |
DEFAULT=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .pdf) \ |
$(addprefix vcs.conf.man,.pdf) \ |
) |
EXTRA=$(addprefix $(distdir)/,vcs.man2html.html vcs.conf.man2html.html) |
ALL=$(DEFAULT) $(EXTRA) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_COMMON=xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" |
# NOT: For manpages the output can be a directory, whereas for XHTML it can not |
DOCBOOK_TO_MAN=$(DOCBOOK_TO_COMMON) -o $(distdir)/ $(DOCBOOK_XSL)/manpages/docbook.xsl |
DOCBOOK_TO_XHTML=$(DOCBOOK_TO_COMMON) $(DOCBOOK_XSL)/xhtml/docbook.xsl |
default: $(DEFAULT) |
extra: $(EXTRA) |
all: $(ALL) |
env: |
@echo --- Values of Makefile variables: --- |
@echo DEFAULT: $(DEFAULT) |
@echo EXTRA: $(EXTRA) |
@echo ALL: $(ALL) |
@echo INTERMEDIATE: $(INTERMEDIATE) |
@echo DOCBOOK_XSL: $(DOCBOOK_XSL) |
@echo DOCBOOK_TO_MAN: $$ $(DOCBOOK_TO_MAN) |
@echo DOCBOOK_TO_XHTML: $$ $(DOCBOOK_TO_XHTML) |
@echo distdir: $(distdir) |
@echo srcdir: $(srcdir) |
@echo ------------------------------------- |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# These are both generated at once |
$(distdir)/vcs.1 $(distdir)/vcs.conf.5: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# Note with both manpages combined the output is combined too |
$(distdir)/vcs.man.html: $(srcdir)/vcs.man.xml |
$(DOCBOOK_TO_XHTML) "$<" > "$@" || ( $(RM) "$@" && false ) |
@# sed post processing: |
@# add CSS link |
@# obfuscate mailto: links |
@# obfuscate emails |
@# replace groff-escaped dots ("\[char46]") |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
-e 's/\\\[char46\]/./g' \ |
"$@" |
@# man2html includes the last revision date, which xsltproc does not, it |
@# seems useful to me, so I'm injecting it here |
sed -i \ |
-e 's!</h1>!</h1>$(shell grep 'Last revision' $< | head -1 \ |
| sed -e 's!.*<date>!!' \ |
-e 's!</date>.*$$!!')!' \ |
"$@" |
##### |
##### RULES SHARING RECIPES |
##### See http://stackoverflow.com/questions/11441084/makefile-with-multiples-rules-sharing-same-recipe |
##### |
# 1/2: Pre-requisites |
$(distdir)/vcs.conf.man.pdf: $(distdir)/vcs.conf.5.pdf |
$(distdir)/vcs.man.pdf: $(distdir)/vcs.1.pdf |
$(distdir)/vcs.man2html.html: $(distdir)/vcs.1 |
$(distdir)/vcs.conf.man2html.html: $(distdir)/vcs.conf.5 |
# 2/2: Common recipe |
$(distdir)/vcs.conf.man.pdf $(distdir)/vcs.man.pdf: |
mv $< $@ |
##### |
##### / END OF RULES SHARING RECIPES |
##### |
$(distdir)/vcs.man2html.html $(distdir)/vcs.conf.man2html.html: |
@# The first two lines of man2html are HTTP headers |
@# The manpage is preprocessed to replace groff-escaped dots (\[char46]) |
sed -e 's/\\\[char46\]/\\./g' "$<" | man2html -r | sed 1,2d > "$@" |
# jade and docbook-to-man conversions don't appear to agree on what's the |
# correct structure, to avoid this here I use doclifter to convert back from |
# man to DocBook, which generates a less semantically-rich but easier to |
# process DocBook file |
$(distdir)/vcs.%.pdf: vcs.% |
doclifter < $< > temp.xml || ( $(RM) temp.xml && false ) |
db2pdf temp.xml || ( $(RM) temp.xml && false ) |
-$(RM) temp.xml |
mv temp.pdf $@ |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.html' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.html, $(DEFAULT)) |
.PHONY: all default extra env clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/rpm/references |
---|
0,0 → 1,21 |
Some useful references: |
"Creating RPM Packages with Fedora" |
<https://fedoraproject.org/wiki/How_to_create_an_RPM_package> |
"Packaging for openSUSE Leap" |
<https://en.opensuse.org/openSUSE:How_to_contribute_to_Leap> |
"Build Service cross distribution howto" |
<https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto> |
"Fedora packaging guidelines" |
<https://fedoraproject.org/wiki/Packaging:Guidelines> |
Alternative requirements: |
As of 2017 there's some conflicting information on boolean operators |
[1] says they are not supported in Requires |
[2] says they are supported in all fields, including requires (rpm >= 4.13) |
1: <https://fedoraproject.org/wiki/Packaging:Guidelines#Rich.2FBoolean_dependencies> |
2: <http://rpm.org/user_doc/boolean_dependencies.html> |
Fedora 25 has RPM 4.13 |
openSUSE Leap 42.2 has RPM 4.11 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/rpm/vcs.spec.in |
---|
0,0 → 1,124 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# originally based on mp3plot's which in turn was based on other sources |
%define is_suse 0%{?suse_version} |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPLv2+ |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
#Requires: rpm >= 4.13 |
#Requires: ( mplayer or ffmpeg ) |
Requires: ffmpeg |
Recommends: mplayer |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
# as per rpmlint: E: wrong-script-interpreter /usr/bin/vcs /usr/bin/env bash |
sed -i '1s@.*@#!/bin/bash@' %buildroot/usr/bin/vcs |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat May 20 2017 - outlyer (at) gmail (dot) com |
- Rewrote dependencies with notes about boolean (alternative) dependecies |
- Depend on ffmpeg and recommend mplayer while distributions catch up with RPM |
- Removed Mandrake detection and updated the macro for SUSE |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/arch/PKGBUILD.in |
---|
0,0 → 1,43 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('mplayer: for better more complete detection/capture' |
'lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/changelog |
---|
0,0 → 1,111 |
vcs (1.13.3-pon.1) experimental; urgency=medium |
* New version |
* debian/control: Added xsltproc to Build-Depends |
* debian/compat: Bumped compatibility level to 9 (jessie) |
* debian/control: Bumped build-dependancy on debhelper to >= 9 |
(compatibility level 9) |
-- Toni Corvera <outlyer@gmail.com> Sat, 20 May 2017 00:04:51 +0200 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/compat |
---|
0,0 → 1,0 |
9 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 9), xsltproc |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/GNUmakefile |
---|
0,0 → 1,131 |
#!/usr/bin/make -f |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n 'Ignore? [y/N] ' ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
@if ! sed -e '/$$Date/!d' dist/vcs | grep -E 'Mon|Tue|Wed|Thu|Fri|Sat|Sun' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from localised SVN export **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n "Ignore? [y/N] " ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
false ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint vcs-$(VER)-*.rpm |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/online_man/Makefile |
---|
0,0 → 1,18 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html |
man.vcs.html: $(docsdir)/vcs.man.html |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
.PHONY: all clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.4-pre.1 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/branches/1.13.2-pre.3+early_color:r664-665 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/tags/1.13.2-pre.4:r666 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/CHANGELOG |
---|
0,0 → 1,523 |
1.13.3 (2017-05-26): |
* Added codec IDs for h.265 and VP9 |
* BUGFIX: Fix handling of failed captures |
* BUGFIX: Fix handling of failed identification |
* BUGFIX: Cleaned output for identification of unsupported file types |
* BUGFIX: Codec information was getting cropped with current versions of |
ImageMagick. Gravity appears to be interpreted in a different way |
now. (Bugfix by Markus) [#323] |
* BUGFIX: Fix incorrect calculation of file size in header. (Based on an |
anonymous patch) [#314] |
* OTHER: Print warning about possible lack of support if no frame could be |
captured |
* OTHER: Don't trust MPlayer's detection of raw video, use FFmpeg's |
detection in such case |
* OTHER: Fix incorrect rendering of Note #1 in vcs.conf's manpage |
* OTHER: Clean up generation and conversion of manpages |
* OTHER: Added versions of MPlayer, FFMpeg, ImageMagick and LSB to debug |
output |
* OTHER: Allow disabling coloured output altogether. [#311] |
This is implemented by honouring $TERM, e.g. "TERM=vt100 vcs ..." |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/vcs |
---|
0,0 → 1,5342 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007-2017 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.3" |
declare -r RELEASE=1 |
declare -ri PRERELEASE=2 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
# Sizes are always rounded up (hence the addition 0.999999 to the fractionary part) |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs_int=$(( $bytes / 1024**3 )) |
local gibs_frac=$(awkex "int($bytes%(1024**3)*100/1024**3 + 0.999999)" ) |
size="$(printf '%d.%02d' $gibs_int $gibs_frac) GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs_int=$(( $bytes / 1024**2 )) |
local mibs_frac=$(awkex "int($bytes%(1024**2)*100/1024**2 + 0.999999)") |
size="$(printf '%d.%02d' $mibs_int $mibs_frac) MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs_int=$(( $bytes / 1024 )) |
local kibs_frac=$(awkex "int($bytes%1024*100/1024 + 0.999999)") |
size="$(printf '%d.%02d' $kibs_int $kibs_frac) KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# First we must find the correct way to query color support. |
# There's basically two variants of tput: |
# terminfo (Linux) and termcap (FreeBSD) |
# These is an issue for portability: |
# - On Linux 'tput colors' is used to query it |
# - On FreeBSD 'tput Co' is used to query it |
# - Linux's tput will fail if it's passed 'Co' |
# - FreeBSD's tput will interpret 'colors' as 'co' and print the number of columns |
local tputc="-1" |
if tput Co >/dev/null 2>&1 ; then |
tputc=$(tput Co) # termcap style |
else |
# Try to guess if it's parsing it as columns |
# The method here is to check against some known terminals |
# pilot: 39 columns mono, pc3: 80 columns, 8 colors |
if [[ 8 = "$(tput -T pc3 colors)" ]]; then |
# colors is interpreted literally |
tputc=$(tput colors) |
fi |
fi |
# Is it able to set colours? |
# Linux's tput can be passed arguments to retrieve the correct escape sequences |
# FreeBSD's tput can not |
if tput bold && [[ "-1" != "$tputc" ]] && tput setaf 0 && tput sgr0; then |
# Can configure completely through tput |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
elif [[ "-1" != "$tputc" ]]; then |
# tput reports color support but it doesn't provide |
# the escape codes directly, will use hardcoded escape codes instead |
HAS_COLORS= |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# NOTE: These messages are supressed if called from classic_identify |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
RESULT= |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} || { |
# Failed capture |
return $? |
} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)." |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
# Allow both the synthetic (for older mplayer) and builtin (for newer mplayer) codec ids |
vcs_hevc|HEVC) vcodec="HEVC" ;; |
vcs_vp9|VP90) vcodec="VP9" ;; # VP9 was detected as rawyuy2 by older MPlayer |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# HEVC and VP9 weren't supported by older versions MPlayer |
# Seen: |
# "hevc (Main) (HEVC / 0x43564548)" in MPEG2-TS |
# "hevc" in h.265 ES |
# TODO: Enforce a minimum version of mplayer |
hevc|hevc\ *) mpid="vcs_hevc" ;; |
vp9) mpid="vcs_vp9" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# and same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
# If the decoder can't seek there's no point in continuing |
if [[ ( ( $DECODER -eq $DEC_FFMPEG ) && ( -z $fflen ) ) || |
( ( $DECODER -eq $DEC_MPLAYER ) && ( -z $mplen ) ) ]]; |
then |
warn "$CAPTURER didn't report a length, seeking won't be possible." |
return $RET_NOLEN |
fi |
[[ -z $fflen ]] && fflen=0 |
[[ -z $mplen ]] && mplen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || { |
# Fall back to ffmpeg's dimensions |
VID[$W]=${FFMPEG_ID[$W]} |
VID[$H]=${FFMPEG_ID[$H]} |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
# MPlayer tends to identify as raw video if it doesn't support the codec |
# fall back to FFmpeg in such case |
if [[ ${MPLAYER_ID[$VCODEC]} = "0x00000000" ]]; then |
VID[$VCODEC]=${FFMPEG_ID[$VCODEC]} |
VID[$VCNAME]=${FFMPEG_ID[$VCNAME]} |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
warn " Does $CAPTURER support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
local ret=$? |
VID=( "${RESULT[@]}" ) |
return $ret |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
local mpplen= |
[[ -z "${MPLAYER_ID[${LEN}]}" ]] || mpplen=$(pretty_stamp ${MPLAYER_ID[$LEN]}) |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $mpplen |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
local clen= |
[[ -z ${VID[$LEN]} ]] || clen=$(pretty_stamp ${VID[$LEN]}) |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $clen |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || { |
exitcode=$? |
[[ ${#capfiles[@]} -gt 0 ]] || { |
# No successful capture, unsupported format? |
# TODO: Adapt to capturer in use |
warn "No successful capture, possible unsupported format." |
} |
return $exitcode |
} |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
# File size rounding |
"get_pretty_size 1127428915 1.05%20GiB #Leading zeroes in file size (GiB)" |
"get_pretty_size 1132462 1.08%20MiB #Leading zeroes in file size (MiB)" |
"get_pretty_size 1116 1.09%20KiB #Leading zeroes in file size (KiB)" |
"get_pretty_size 1889785610 1.76%20GiB #Pretty-printed file size (GiB)" |
"get_pretty_size 762650296 727.32%20MiB #Pretty-printed file size (MiB)" |
"get_pretty_size 524810 512.51%20KiB #Pretty-printed file size (KiB)" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
val=${val/\%20/ } |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Expected '$val'. Got '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2017 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
NOTE: If you have any configuration loaded before this |
takes effect the script might still print some |
colour. You can disable it completely by setting |
the TERM variable to a monochrome term type, e.g.: |
$ env TERM=vt100 vcs [options] |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
if type -paf lsb_release >/dev/null ; then |
lsb_release=$(lsb_release -d | cut -d: -f2- | sed 's/^[[:space:]]*//') |
fi |
imversion=$(convert --version | head -1 | cut -d' ' -f2-) |
if [[ -n "$MPLAYER_BIN" ]]; then |
mpversion=$("$MPLAYER_BIN" --version 2>/dev/null || true) |
# Older mplayer doesn't understand --version... |
if grep "Unknown option" <<<"$mpversion" ; then |
# ...But the last output line contains the version in my sample |
mpversion=$(tail -1 <<<"$mpversion") |
fi |
fi |
if [[ -n "$FFMPEG_BIN" ]]; then |
# Older versions print to stderr, newer to stdout |
ffversion=$("$FFMPEG_BIN" -version 2>&1 | head -1) |
lavcversion=$("$FFMPEG_BIN" -version 2>&1 | grep libavcodec \ |
| sed 's/[[:space:]][[:space:]]*/ /') |
ffversion="$ffversion / $lavcversion" |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
MPlayer: $mpversion |
FFMpeg: $ffversion |
ImageMagick: $imversion |
LSB Description: $lsb_release |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/docs/src/plain_messages_note.man.inc.xml |
---|
0,0 → 1,16 |
<!DOCTYPE para PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
<!-- This file is meant to be included, it contains the explanation |
about handling of colourised (console) output, to be shared |
by both the documentation of -Wc and plain_messages. --> |
<para> |
<note> |
<para>Some colour will be printed by default until this option is handled. |
If you need to completely disable colour, e.g. to run in cron jobs, you can |
do so by setting an appropriate TERM environment variable e.g. |
</para> |
<!-- Note: literallayout allows injection of linebreaks (Docbook has no <br /> but HTML output doesn't come out too pretty--> |
<para><literal>$ <command>TERM=<replaceable>vt100</replaceable> vcs</command></literal></para> |
<para>will make the script switch to monochrome output altogether.</para> |
</note> |
</para> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,594 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date$ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,1085 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5.1: The Definitive Guide (includes list of elements) |
<http://tdg.docbook.org/tdg/5.1/> |
- ": refentry - A reference page (originally a UNIX man-style reference page). |
<http://tdg.docbook.org/tdg/5.1/refentry.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man ./vcs.1 | less |
or |
$ man ./vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). |
"Video Contact Sheet *NIX User Manual" is too long and |
"Manual" is cropped off --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
<!-- for vcs.conf(5) --> |
<!ENTITY title "Video Contact Sheet *NIX"> |
<!ENTITY confpackage "vcs.conf"> |
<!ENTITY confsection "5"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!-- END OF DOCTYPE --> |
<reference><!-- Group of refentry's --> |
<!-- This is required by reference to validate with e.g. xmlto. |
NOTE This appears to override the title set below. --> |
<title>&title;</title> |
<!-- START OF man(1) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<date>Last revision: 2017-05-23</date><!-- Exported on: $Date$ --> |
<revhistory> |
<revision> |
<date>2017-05-23</date> |
<revremark>Added note about disabling colour output (starting with 1.13.3).</revremark> |
</revision> |
<revision> |
<date>2011-08-29</date> |
</revision> |
</revhistory> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="option_wc"><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./plain_messages_note.man.inc.xml" /> |
</listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><envar>TERM</envar></term> |
<listitem> |
<para>Affects the usage of colour output to console being on or off |
by default. See the documentation for <option><xref linkend="option_wc" /></option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- END OF vcs(1) --> |
<!-- START OF vcs.conf(5) --> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&confpackage;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2017</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
<date>Last revision: 2011-08-29</date> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&confpackage;</refentrytitle> |
<manvolnum>&confsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&confpackage;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<!-- Note: \[char46] is a escaped dot for groff. |
Otherwise this paragraph's output will start a line |
with a dot, which makes groff/man interpret it as an |
inexistent macro, |
i.e. the filename won't render at all |
Reference: http://stackoverflow.com/questions/11469341/ |
--> |
<para><filename>\[char46]/vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- END OF vcs.conf(5) --> |
</reference> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/docs/GNUmakefile |
---|
0,0 → 1,134 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
# Since 1.13.3 the man pages are combined into a single input |
# The XHTML output contains both man pages as a side effect, while the groff |
# and PDF output are separate for each man page. TeX output was removed from |
# this makefile. |
DEFAULT=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .pdf) \ |
$(addprefix vcs.conf.man,.pdf) \ |
) |
EXTRA=$(addprefix $(distdir)/,vcs.man2html.html vcs.conf.man2html.html) |
ALL=$(DEFAULT) $(EXTRA) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_COMMON=xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" |
# NOT: For manpages the output can be a directory, whereas for XHTML it can not |
DOCBOOK_TO_MAN=$(DOCBOOK_TO_COMMON) -o $(distdir)/ $(DOCBOOK_XSL)/manpages/docbook.xsl |
DOCBOOK_TO_XHTML=$(DOCBOOK_TO_COMMON) $(DOCBOOK_XSL)/xhtml/docbook.xsl |
default: $(DEFAULT) |
extra: $(EXTRA) |
all: $(ALL) |
env: |
@echo --- Values of Makefile variables: --- |
@echo DEFAULT: $(DEFAULT) |
@echo EXTRA: $(EXTRA) |
@echo ALL: $(ALL) |
@echo INTERMEDIATE: $(INTERMEDIATE) |
@echo DOCBOOK_XSL: $(DOCBOOK_XSL) |
@echo DOCBOOK_TO_MAN: $$ $(DOCBOOK_TO_MAN) |
@echo DOCBOOK_TO_XHTML: $$ $(DOCBOOK_TO_XHTML) |
@echo distdir: $(distdir) |
@echo srcdir: $(srcdir) |
@echo ------------------------------------- |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# These are both generated at once |
$(distdir)/vcs.1 $(distdir)/vcs.conf.5: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# Note with both manpages combined the output is combined too |
$(distdir)/vcs.man.html: $(srcdir)/vcs.man.xml |
$(DOCBOOK_TO_XHTML) "$<" > "$@" || ( $(RM) "$@" && false ) |
@# sed post processing: |
@# add CSS link |
@# obfuscate mailto: links |
@# obfuscate emails |
@# replace groff-escaped dots ("\[char46]") |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
-e 's/\\\[char46\]/./g' \ |
"$@" |
@# man2html includes the last revision date, which xsltproc does not, it |
@# seems useful to me, so I'm injecting it here |
sed -i \ |
-e 's!</h1>!</h1>$(shell grep 'Last revision' $< | head -1 \ |
| sed -e 's!.*<date>!!' \ |
-e 's!</date>.*$$!!')!' \ |
"$@" |
##### |
##### RULES SHARING RECIPES |
##### See http://stackoverflow.com/questions/11441084/makefile-with-multiples-rules-sharing-same-recipe |
##### |
# 1/2: Pre-requisites |
$(distdir)/vcs.conf.man.pdf: $(distdir)/vcs.conf.5.pdf |
$(distdir)/vcs.man.pdf: $(distdir)/vcs.1.pdf |
$(distdir)/vcs.man2html.html: $(distdir)/vcs.1 |
$(distdir)/vcs.conf.man2html.html: $(distdir)/vcs.conf.5 |
# 2/2: Common recipe |
$(distdir)/vcs.conf.man.pdf $(distdir)/vcs.man.pdf: |
mv $< $@ |
##### |
##### / END OF RULES SHARING RECIPES |
##### |
$(distdir)/vcs.man2html.html $(distdir)/vcs.conf.man2html.html: |
@# The first two lines of man2html are HTTP headers |
@# The manpage is preprocessed to replace groff-escaped dots (\[char46]) |
sed -e 's/\\\[char46\]/\\./g' "$<" | man2html -r | sed 1,2d > "$@" |
# jade and docbook-to-man conversions don't appear to agree on what's the |
# correct structure, to avoid this here I use doclifter to convert back from |
# man to DocBook, which generates a less semantically-rich but easier to |
# process DocBook file |
$(distdir)/vcs.%.pdf: vcs.% |
doclifter < $< > temp.xml || ( $(RM) temp.xml && false ) |
db2pdf temp.xml || ( $(RM) temp.xml && false ) |
-$(RM) temp.xml |
mv temp.pdf $@ |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.html' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.html, $(DEFAULT)) |
.PHONY: all default extra env clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/rpm/references |
---|
0,0 → 1,21 |
Some useful references: |
"Creating RPM Packages with Fedora" |
<https://fedoraproject.org/wiki/How_to_create_an_RPM_package> |
"Packaging for openSUSE Leap" |
<https://en.opensuse.org/openSUSE:How_to_contribute_to_Leap> |
"Build Service cross distribution howto" |
<https://en.opensuse.org/openSUSE:Build_Service_cross_distribution_howto> |
"Fedora packaging guidelines" |
<https://fedoraproject.org/wiki/Packaging:Guidelines> |
Alternative requirements: |
As of 2017 there's some conflicting information on boolean operators |
[1] says they are not supported in Requires |
[2] says they are supported in all fields, including requires (rpm >= 4.13) |
1: <https://fedoraproject.org/wiki/Packaging:Guidelines#Rich.2FBoolean_dependencies> |
2: <http://rpm.org/user_doc/boolean_dependencies.html> |
Fedora 25 has RPM 4.13 |
openSUSE Leap 42.2 has RPM 4.11 |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/rpm/vcs.spec.in |
---|
0,0 → 1,124 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# originally based on mp3plot's which in turn was based on other sources |
%define is_suse 0%{?suse_version} |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPLv2+ |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
#Requires: rpm >= 4.13 |
#Requires: ( mplayer or ffmpeg ) |
Requires: ffmpeg |
Recommends: mplayer |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
# as per rpmlint: E: wrong-script-interpreter /usr/bin/vcs /usr/bin/env bash |
sed -i '1s@.*@#!/bin/bash@' %buildroot/usr/bin/vcs |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat May 20 2017 - outlyer (at) gmail (dot) com |
- Rewrote dependencies with notes about boolean (alternative) dependecies |
- Depend on ffmpeg and recommend mplayer while distributions catch up with RPM |
- Removed Mandrake detection and updated the macro for SUSE |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/arch/PKGBUILD.in |
---|
0,0 → 1,43 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('mplayer: for better more complete detection/capture' |
'lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/changelog |
---|
0,0 → 1,111 |
vcs (1.13.3-pon.1) experimental; urgency=medium |
* New version |
* debian/control: Added xsltproc to Build-Depends |
* debian/compat: Bumped compatibility level to 9 (jessie) |
* debian/control: Bumped build-dependancy on debhelper to >= 9 |
(compatibility level 9) |
-- Toni Corvera <outlyer@gmail.com> Sat, 20 May 2017 00:04:51 +0200 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/compat |
---|
0,0 → 1,0 |
9 |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 9), xsltproc |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.3/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/GNUmakefile |
---|
0,0 → 1,131 |
#!/usr/bin/make -f |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n 'Ignore? [y/N] ' ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
@if ! sed -e '/$$Date/!d' dist/vcs | grep -E 'Mon|Tue|Wed|Thu|Fri|Sat|Sun' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from localised SVN export **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
echo -n "Ignore? [y/N] " ; \ |
read RESPONSE ; [ "$$RESPONSE" = 'y' ] || [ "$$RESPONSE" = 'Y' ] ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
false ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
@# Don't fail even if rpmlint does. It fails with no signature on Debian |
-rpmlint vcs-$(VER)-*.rpm |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/online_man/Makefile |
---|
0,0 → 1,18 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html |
man.vcs.html: $(docsdir)/vcs.man.html |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
.PHONY: all clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.3/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.3/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/branches/1.13.2-pre.3+early_color:r664-665 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/tags/1.13.2-pre.4:r666 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2016 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/CHANGELOG |
---|
0,0 → 1,503 |
1.13.2 (?): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
* OTHER: Allow disabling coloured output altogether. [#311] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/vcs |
---|
0,0 → 1,5236 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.2" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=4 # This version is retrofitted, it was never actually made public |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
# NOTE: This pre-release is a quick modification, it just adds a way of disabling |
# coloured output altogether, by setting TERM to a monochrome terminal type. |
# e.g. $ env TERM=vt100 vcs -Wc |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && [[ "-1" != "$(tput colors)" ]] && tput setaf 0 && tput sgr0; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2016 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/changelog |
---|
0,0 → 1,101 |
vcs (1.13.2-pon.1) UNRELEASED; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 16 May 2014 17:09:00 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/Makefile |
---|
0,0 → 1,114 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.4 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/CHANGELOG |
---|
0,0 → 1,515 |
1.13.3 (?): |
* Added codec IDs for h.265 and VP9 |
* BUGFIX: Fix handling of failed captures |
* BUGFIX: Fix handling of failed identification |
* BUGFIX: Cleaned output for identification of unsupported file types |
* BUGFIX: Codec information was getting cropped with current versions of |
ImageMagick. Gravity appears to be interpreted in a different way |
now. (Bugfix by Markus) [#323] |
* OTHER: Print warning about possible lack of support if no frame could be |
captured |
* OTHER: Don't trust MPlayer's detection of raw video, use FFmpeg's |
detection in such case |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/vcs |
---|
0,0 → 1,5272 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007-2017 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.3" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=2 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# NOTE: These messages are supressed if called from classic_identify |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
RESULT= |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} || { |
# Failed capture |
return $? |
} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)." |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
vcs_hevc) vcodec="HEVC" ;; # Not yet supported by MPlayer |
vcs_vp9) vcodec="VP9" ;; # Detected as rawyuy2 by MPlayer |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
# Seen: |
# "hevc (Main) (HEVC / 0x43564548)" in MPEG2-TS |
# "hevc" in h.265 ES |
hevc|hevc\ *) mpid="vcs_hevc" ;; # Not supported by MPlayer as of this writing |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
vp9) mpid="vcs_vp9" ;; # Not supported by MPlayer? |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# and same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
# If the decoder can't seek there's no point in continuing |
if [[ ( ( $DECODER -eq $DEC_FFMPEG ) && ( -z $fflen ) ) || |
( ( $DECODER -eq $DEC_MPLAYER ) && ( -z $mplen ) ) ]]; |
then |
warn "$CAPTURER didn't report a length, seeking won't be possible." |
return $RET_NOLEN |
fi |
[[ -z $fflen ]] && fflen=0 |
[[ -z $mplen ]] && mplen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || { |
# Fall back to ffmpeg's dimensions |
VID[$W]=${FFMPEG_ID[$W]} |
VID[$H]=${FFMPEG_ID[$H]} |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
# MPlayer tends to identify as raw video if it doesn't support the codec |
# fall back to FFmpeg in such case |
if [[ ${MPLAYER_ID[$VCODEC]} = "0x00000000" ]]; then |
VID[$VCODEC]=${FFMPEG_ID[$VCODEC]} |
VID[$VCNAME]=${FFMPEG_ID[$VCNAME]} |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
warn " Does $CAPTURER support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
local ret=$? |
VID=( "${RESULT[@]}" ) |
return $ret |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
local mpplen= |
[[ -z "${MPLAYER_ID[${LEN}]}" ]] || mpplen=$(pretty_stamp ${MPLAYER_ID[$LEN]}) |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $mpplen |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
local clen= |
[[ -z ${VID[$LEN]} ]] || clen=$(pretty_stamp ${VID[$LEN]}) |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $clen |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || { |
exitcode=$? |
[[ ${#capfiles[@]} -gt 0 ]] || { |
# No successful capture, unsupported format? |
# TODO: Adapt to capturer in use |
warn "No successful capture, possible unsupported format." |
} |
return $exitcode |
} |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2017 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2017 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/changelog |
---|
0,0 → 1,101 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/Makefile |
---|
0,0 → 1,115 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/vcs |
---|
0,0 → 1,5230 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2017 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.3" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=1 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity NorthEast -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2014 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/changelog |
---|
0,0 → 1,101 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/CHANGELOG |
---|
0,0 → 1,502 |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2014 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/Makefile |
---|
0,0 → 1,115 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.3-pre.1 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/changelog |
---|
0,0 → 1,101 |
vcs (1.13.2-pon.1) experimental; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 18 May 2014 17:41:44 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/CHANGELOG |
---|
0,0 → 1,502 |
1.13.2 (2014-05-18): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/vcs |
---|
0,0 → 1,5230 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.2" |
declare -r RELEASE=1 |
declare -ri PRERELEASE=3 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2014 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2014 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.2/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/Makefile |
---|
0,0 → 1,115 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: vcs-$(VER).tar.gz |
ln -sf vcs-$(VER).tar.gz vcs_$(VER).orig.tar.gz |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.2/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.2/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.2:r576-582 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/changelog |
---|
0,0 → 1,101 |
vcs (1.13.2-pon.1) UNRELEASED; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 16 May 2014 17:09:00 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/CHANGELOG |
---|
0,0 → 1,502 |
1.13.2 (?): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/vcs |
---|
0,0 → 1,5230 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.2" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=3 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2014 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2014 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/Makefile |
---|
0,0 → 1,114 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.3 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/CHANGELOG |
---|
0,0 → 1,499 |
1.13.2 (?): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/vcs |
---|
0,0 → 1,5230 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.2" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=2 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '$to' ]]" |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2014 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/changelog |
---|
0,0 → 1,95 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2014 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/Makefile |
---|
0,0 → 1,114 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.2-pre.2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/changelog |
---|
0,0 → 1,95 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/CHANGELOG |
---|
0,0 → 1,495 |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2014 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/vcs |
---|
0,0 → 1,5224 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.1" |
declare -r RELEASE=1 |
declare -ri PRERELEASE=1 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '$to' ]]" |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $FUNCNAME $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2014 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13.1/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/Makefile |
---|
0,0 → 1,114 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13.1/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13.1/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13.1 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.13/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/Makefile |
---|
0,0 → 1,114 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/tags/1.13/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/tags/1.13/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/CHANGELOG |
---|
0,0 → 1,489 |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/vcs |
---|
0,0 → 1,5224 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13" |
declare -r RELEASE=1 |
declare -ri PRERELEASE=2 |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '$to' ]]" |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $FUNCNAME $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2013 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/AUTHORS |
---|
0,0 → 1,9 |
Copyright 2007-2013 Toni Corvera |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/changelog |
---|
0,0 → 1,89 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.13/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.13/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/tags/1.13/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/tags/1.13/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/tags/1.13/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/tags/1.13/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/tags/1.13/dist/arch/PKGBUILD.in |
---|
0,0 → 1,39 |
# |
# $Rev$ |
# |
# Maintainer (Upstream): Toni Corvera <outlyer@gmail.com> |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
build() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.13 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/vcs |
---|
0,0 → 1,4645 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.12.3" |
declare -r RELEASE=1 |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT=1, --posix or |
#+ --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --undocumented shoehorn -> NONE 1.12 1.13 |
# --funky -> --profile ? ?+1 |
# --end_offset -> --end-offset 1.12 (silent), 1.13 (warn) 1.14 |
# (new var names introduced in 1.12) 1.12 1.14 |
# > Usage of ./vcs.conf is also deprecated since it doesn't mesh well with profiles |
# (and it was a bad placeholder for them). Will remain to be loadable with |
# -C:pwd. |
# Loaded by default up to 1.13 (with warning in 1.13). |
# Not loaded from 1.14 onwards |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# * Optimisations: |
# - Reduce the number of forks |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
declare -r TAB=$'\011' # Tab |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=Black # Font colour for meta info box |
declare fg_sign=Black # Font colour for signature |
declare fg_tstamps=White # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -r DEFAULT_END_OFFSET="5.5%" |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $FONT_MINCHO to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height='100%' |
declare interval=$DEFAULT_INTERVAL # Interval of captures (~length/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (~length/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This amount of time is *not* captured from the end of the video |
declare end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
# TODO: Allow 'y', 'n' in booleans |
# TODO: Remove extra coherence_check()'s once constraints are implemented |
declare -ra OVERRIDE_MAP=( |
"user:::" |
"extended_factor:=:=:f" |
"stdout::" |
"stderr::" |
"DEBUG:=:=:b" |
"interval:=:=:t" |
"numcaps:=:=:p" |
"captures:numcaps:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"columns:cols:=:p" |
"cols:=:alias:p" # Alias |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"bg_heading::" |
"bg_sign::" |
"bg_title::" |
"bg_contact::" |
"bg_tstamps::" |
"fg_heading::" |
"fg_sign::" |
"fg_tstamps::" |
"fg_title::" |
"font_heading::" |
"font_sign::" |
"font_tstamps::" |
"font_title::" |
"font_all:=:meta" # see parse_override |
"bg_all:=:meta" |
"fg_all:=:meta" |
"pts_tstamps::" |
"pts_meta::" |
"pts_sign::" |
"pts_title::" |
# Aliases for cosmetic stuff |
"bg_header:bg_heading:alias" |
"bg_signature:bg_sign:alias" |
"bg_footer:bg_sign:alias" |
"bg_sheet:bg_contact:alias" |
"fg_header:fg_heading:alias" |
"fg_signature:fg_sign:alias" |
"fg_footer:fg_sign:alias" |
"font_header:font_heading:alias" |
"font_meta:font_heading:alias" |
"font_signature:font_sign:alias" |
"font_footer:font_sign:alias" |
"pts_heading:pts_meta:alias" |
"pts_header:pts_meta:alias" |
"pts_signature:pts_sign:alias" |
"pts_footer:pts_sign:alias" |
"signature:user_signature:" |
"user_signature::deprecated=signature" |
"quality:output_quality:=:n" |
"output_quality::deprecated=quality:n" |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"decoder:=:=:D" |
#"capture_mode:timecode_from:alias:T" |
"timecode_from:=:=:T" |
"verbosity:=:=:V" |
"format:output_format:" |
"output_format:=:deprecated=format" |
"simple_feedback:plain_messages:=:b" |
"plain_messages::deprecated=simple_feedback:b" |
"height:th_height:=:h" |
"th_height::deprecated=height:h" |
"padding:HPAD:=:n" |
"HPAD:=:deprecated=padding:n" |
"nonlatin_font:FONT_MINCHO:" |
"FONT_MINCHO::deprecated=nonlatin_font" |
"NONLATIN_FILENAMES:=:=:b" |
"end_offset:=:=:I" # New, used to have a two-variables assignment before USR_* |
"DEFAULT_END_OFFSET:end_offset:deprecated=end_offset:I" |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
"shoehorned::striked" |
"safe_rename_pattern::striked" |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f "$cfgfile" ]] || continue |
load_config_file "$cfgfile" |
done |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL"\ |
" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
local n=$1 v=$2 p=$3 |
# Get constraint |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$n:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
esac |
if [[ $checkfn ]] && ! $checkfn "$v" ; then |
[[ $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr '[A-Z]' '[a-z]') |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//') |
buffered warn "Variable '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Variable '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Variable '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="$ivar='$varval'; USR_$ivar='$varval'" |
eval "$evcode" |
fi |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
case "$1" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "font_heading=$2" |
parse_override "font_sign=$2" |
parse_override "font_title=$2" |
parse_override "font_tstamps=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "fg_heading=$2" |
parse_override "fg_sign=$2" |
parse_override "fg_tstamps=$2" |
parse_override "fg_title=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "bg_heading=$2" |
parse_override "bg_contact=$2" |
parse_override "bg_sign=$2" |
parse_override "bg_title=$2" |
parse_override "bg_tstamps=$2" |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'decoder=$DEC_FFMPEG' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
is_float() { local P='^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
is_percentage() { |
local P='^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
### Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[A-Z]' '[a-z]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
#grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
expr match "$1" '.*\.\(.*\)' |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
local ERE='\.' |
[[ $secs && ( $secs =~ $ERE ) ]] && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[[ $secs ]] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkexf "$t" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' && '$t' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully soon this won't be needed |
sanitise_rename_pattern() { |
# No quoting! => Regex |
if [[ ! $safe_rename_pattern =~ %e || ! $safe_rename_pattern =~ %N || \ |
! $safe_rename_pattern =~ %b ]]; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if [[ $safe_rename_pattern =~ \# ]]; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$safe_rename_pattern") |
(( n++ )); |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG_BIN='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER_BIN='' |
nopng=1 |
fi |
} |
[[ ( -n $MPLAYER_BIN ) || ( -n $FFMPEG_BIN ) ]] || { |
local pngwarn= |
[[ $nopng -eq 1 ]] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
(( retval++ ,1 )) |
} |
if [[ ( $decoder -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
decoder=$DEC_MPLAYER |
elif [[ ( $decoder -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $verbosity -ge $V_ERROR ]]; then |
[[ $plain_messages -eq 0 ]] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $verbosity -ge $V_WARN ]]; then |
[[ $plain_messages -eq 0 ]] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $verbosity -ge $V_INFO ]]; then |
[[ $plain_messages -eq 0 ]] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $verbosity -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
echo "[TRACE]: $(caller 0 | cut -d' ' -f2) $*" >&2 |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == $ref ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN:$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
while [[ $cal ]]; do |
cal=${cal% *} |
error " ${cal#* }:${cal% *}" |
cal=$(caller $level) |
(( level++ )) |
done |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if is_percentage $end_offset ; then |
eff_eo=$(percent $end $end_offset) |
else |
eff_eo=$(get_interval "$end_offset") |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_end_offset ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
inf "Capturing in range [$(pretty_stamp $inc)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second, $3 = output file) |
capture() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
# globals: $shoehorned $decoder |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
local key= |
# Normalise key values' decimals |
if [[ $decoder -eq $DEC_MPLAYER ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES") |
if [[ $cached ]]; then |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
return $? |
fi |
assert '[[ $decoder -eq $DEC_MPLAYER || $decoder -eq $DEC_FFMPEG ]]' |
if [[ $decoder -eq $DEC_MPLAYER ]]; then |
mplayer_capture "$f" "$ofile" "$stamp" |
elif [[ $decoder -eq $DEC_FFMPEG ]]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
ffmpeg_capture "$f" "$ofile" "$stamp" |
fi || { |
[[ $decoder -ne $DEC_MPLAYER ]] || stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
} |
CAPTURES="$CAPTURES$key:$ofile$NL" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [[ $height -lt 200 ]]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [[ $repeat -gt 1 ]]; do |
in="$in '$base_reel' " |
(( repeat-- )); |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $decoder -eq $DEC_MPLAYER ]]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! mplayer_capture "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [[ $decoder -eq $DEC_FFMPEG ]]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! ffmpeg_capture "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# {{{{ # Mplayer support |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
[[ $MPLAYER_BIN ]] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$stderr" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$stderr" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = output file, $3 = timestamp[, $4 = extra opts]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$2 |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [[ $DVD_MODE -eq 1 ]]; then |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
mvq "$cap" "$o" |
} |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
[[ $FFMPEG_BIN ]] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ ( $DVD_MODE -eq 0 ) || ( $DVD_MOUNTP ) ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
FFMPEG_ID=("${fi[@]}") |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[[ $MPLAYER_BIN ]] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
assert '[[ $MPLAYER_BIN || $FFMPEG_BIN ]]' |
# Classic mode, use both mplayer and ffmpeg when available |
if [[ $MPLAYER_BIN && $FFMPEG_BIN ]]; then |
# By default take mplayer's values |
VID=("${MPLAYER_ID[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${FFMPEG_ID[$FPS]} |
[[ ( -n ${MPLAYER_ID[$FPS]} ) && ( -n ${FFMPEG_ID[$FPS]} ) ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${FFMPEG_ID[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${FFMPEG_ID[$CHANS]} ]] && VID[$CHANS]=${FFMPEG_ID[$CHANS]} |
[[ ${FFMPEG_ID[$ASPECT]} ]] && VID[$ASPECT]=${FFMPEG_ID[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${FFMPEG_ID[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [[ $MPLAYER_BIN ]]; then |
# Must do with mplayer only... |
VID=("${MPLAYER_ID[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${VID[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${VID[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [[ $FFMPEG_BIN ]]; then |
# Must do with mplayer only... |
VID=("${FFMPEG_ID[@]}") |
# So far I know of no weird results. Yet. |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [[ $FFMPEG_BIN ]]; then |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $decoder -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $manual_mode -eq 1 ) && ( -z $initial_stamps ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$extended_factor" -eq 0 ; then |
extended_factor=0 |
fi |
if [[ ( $decoder -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [[ ( $decoder -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [[ $DVD_MODE -eq 1 ]]; then |
# Since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $decoder -ne $DEC_MPLAYER ]]; then |
if [[ $MPLAYER_BIN ]]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $FONT_MINCHO ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
FONT_MINCHO="$font_heading" |
} |
fi |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_font_heading && $USR_font_title && \ |
$USR_font_tstamps && $USR_font_sign ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_font_heading ) && ( $font_heading != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_font_title ) && ( $font_title != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_font_tstamps ) && ( $font_tstamps != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_font_sign ) && ( $font_sign != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_font_heading ]] && font_heading="$dvs" |
[[ -z $USR_font_title ]] && font_title="$dvs" |
[[ -z $USR_font_tstamps ]] && font_tstamps="$dvs" |
[[ -z $USR_font_sign ]] && font_sign="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" && [[ $th_height != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${th_height}) |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $aspect_ratio -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && aspect_ratio=0 || aspect_ratio=-1 |
elif [[ $aspect_ratio -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkexf "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $aspect_ratio -eq -1 ]]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $manual_mode -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${initial_stamps[@]}" ) |
else |
TIMECODES=( "${initial_stamps[@]}" ) |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $decoder -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" $stamp "$hlcapfile" || return $? |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $cols ]]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" $stamp "$tfile" || return $? |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $cols ]]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $extended_factor != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $extended_factor)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp "$capfile" || return $? |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'cols*2' ]]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $extended_factor != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $extended_factor != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $extended_factor != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2 = ; (( exw2=(width - exv) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $anonymous_mode -eq 0 ]]; then |
signature="$user_signature $user${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $title ]]; then |
local tlheight=$(line_height "$font_title" "$pts_title") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font="$font_heading" |
else |
fn_font="$FONT_MINCHO" |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$font_heading" "$pts_meta") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != $font_heading ]]; then |
local fnlineheight=$lineheight |
fnlineheight=$(line_height "$fn_font" "$pts_meta") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$font_sign" "$pts_sign") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$output_format" |
if [[ $output_format != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( retval++ ,1 )) # The ,1 ensures '((...))' doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[[ $HAS_COLORS ]] || inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2011 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $decoder == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $decoder == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr '[a-z]' '[A-Z]') f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case "$t" in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
interval=$(get_interval $2) |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
numcaps=$2 |
timecode_from=$TC_NUMCAPS |
USR_numcaps=$2 |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 = '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
username=$(id -un) |
if type -p getent >/dev/null ; then |
user=$(getent passwd "$username" | cut -d':' -f5 |sed 's/,.*//g') |
else |
user=$(grep "^$username:" /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
user=$username |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset|--end-offset) |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
end_offset="$2" |
else |
end_offset=$(get_interval "$2") |
fi |
USR_end_offset="$end_offset" |
unset is_i |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M|--mplayer) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_FONT_MINCHO ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [[ $2 ]] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_FONT_MINCHO ]]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if [[ $2 =~ 'GETOPT=' ]] ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if echo "$2" | grep -q '^:' ; then |
if [[ $2 = ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $HPAD -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $verbosity -gt $V_ERROR ]]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
USR_verbosity=$verbosity |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
error "Shoehorning of options is scheduled to be removed in the next version." |
error " Please contact the author if you absolutely need it." |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
*) false ;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
[[ $decoder -eq $DEC_MPLAYER ]] && d='mplayer' |
[[ $decoder -eq $DEC_FFMPEG ]] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $verbosity -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $verbosity -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,113 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
# Manpage |
%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/CHANGELOG |
---|
0,0 → 1,420 |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs -dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,39 |
# |
# $Rev$ |
# |
# Maintainer (Upstream): Toni Corvera <outlyer@gmail.com> |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
build() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/changelog |
---|
0,0 → 1,82 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman vcs.1 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/docs |
---|
0,0 → 1,0 |
examples/vcs.conf.example |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/common.mk |
---|
0,0 → 1,72 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man/man1/ |
all: |
# Nothing to be done |
dist: vcs.spec |
# Files installed in packages but not outside |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR) |
install -m644 vcs.1 $(DESTDIR)$(MANDIR)/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/vcs.1 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR) |
examples/vcs.conf.example: examples/vcs.conf |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
-$(RM) examples/vcs.conf.example |
distclean: clean |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/profiles/black.conf |
---|
0,0 → 1,9 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/profiles/white.conf |
---|
0,0 → 1,9 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/BSDmakefile |
---|
0,0 → 1,14 |
VERSION!=head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/' |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1 |
.endif |
.if empty($(RM)) |
RM=rm -f |
.endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/GNUmakefile |
---|
0,0 → 1,13 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | sed -e 's/.*"\(.*\)".*/\1/') |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/pkg/examples/vcs.conf |
---|
0,0 → 1,152 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
# $Rev$ # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/Makefile |
---|
0,0 → 1,72 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
pkg/vcs.1: manpage.xml |
xmlto -o pkg man $< |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
pkg/vcs.1 \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd pkg && ln -s ../vcs-$(VER).tar.gz ./ |
cd pkg && make PKGBUILD |
$(RM) pkg/vcs-$(VER).tar.gz |
mv pkg/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/manpage.xml |
---|
0,0 → 1,382 |
<?xml version='1.0' encoding='UTF-8'?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
This file based on the default template for debian packages. |
Generation of man page: |
$ xmlto man manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections |
read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be |
found in the docbook-xsl-doc-html package. |
Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` |
General documentation about man-pages and man-page-formatting: |
man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ |
--> |
<!-- Fill in your name for FIRSTNAME and SURNAME. --> |
<!ENTITY dhfirstname "Toni"> |
<!ENTITY dhsurname "Corvera"> |
<!-- dhusername could also be set to "&dhfirstname; &dhsurname;". --> |
<!ENTITY dhusername "Toni Corvera"> |
<!ENTITY dhemail "outlyer@gmail.com"> |
<!ENTITY dhsection "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY dhtitle "vcs User Manual"> |
<!ENTITY dhucpackage "VCS"> |
<!ENTITY dhpackage "vcs"> |
]> |
<refentry> |
<refentryinfo> |
<title>&dhtitle;</title> |
<productname>&dhpackage;</productname> |
<authorgroup> |
<author> |
<firstname>&dhfirstname;</firstname> |
<surname>&dhsurname;</surname> |
<contrib>VCS author.</contrib> |
<address> |
<email>&dhemail;</email> |
</address> |
</author> |
</authorgroup> |
<copyright> |
<year>2007-2010</year> |
<holder>&dhusername;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<edition>$Rev$</edition> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&dhucpackage;</refentrytitle> |
<manvolnum>&dhsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&dhpackage;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<arg choice="opt"><option>options</option></arg> |
<arg choice="plain"><replaceable>FILE</replaceable></arg> |
<arg choice="opt"><replaceable>FILE</replaceable> |
<arg choice="opt"><replaceable>...</replaceable></arg> |
</arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<arg choice="opt"> |
<option>-n<parameter>20</parameter></option> |
<option>-c<parameter>4</parameter></option> |
</arg> |
<arg choice="plain"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<arg choice="opt"><option>--output=<parameter>OUTPUT1</parameter></option></arg> |
<arg choice="opt"><option>--output=<parameter>OUTPUT2</parameter></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt"><replaceable>INPUT2</replaceable> |
<arg choice="opt"><replaceable>...</replaceable></arg> |
</arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<!-- Normally the help and version options make the programs stop |
right after outputting the requested information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<!-- FIXME --> |
<para>This is a work in progress manual page for <command>&dhpackage;</command>, |
use <parameter>--help</parameter>, <parameter>--fullhelp</parameter> |
and the online documentation (located at <ulink url="http://p.outlyer.net/dox/vcs" />) |
for further usage instructions.</para> |
<para><command>&dhpackage;</command> is a program that creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
</refsect1> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain. Changes the mode of operation |
to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>interval</replaceable></option></term> |
<term><option>--interval=<replaceable>interval</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures. Changes the mode of operation |
to capture at fixed intervals. The number of captures will depend |
on the video length.</para> |
<para>See <xref linkend="interval_format" /> for the allowed syntax.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>number</replaceable></option></term> |
<term><option>--columns=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet. The number of rows |
will depend on this and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>height</replaceable></option></term> |
<term><option>--height=<replaceable>height</replaceable></option></term> |
<listitem> |
<para>Height of captures. Can be a number (of pixels) or a percentage |
(of the video height). By default the same size as the video is used.</para> |
<para>The width is derived from height and aspect ratio.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>filename</replaceable></option></term> |
<term><option>--output=<replaceable>filename</replaceable></option></term> |
<listitem> |
<para>Name of output file. By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> and |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<!-- |
<varlistentry> |
<term><option>-x <replaceable>t</replaceable></option></term> |
<term><option>- -xy=<replaceable>t</replaceable></option></term> |
<listitem> |
<para>DESC.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&dhpackage;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&dhpackage;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${PWD}/vcs.conf</filename></term> |
<listitem> |
<para>The per-directory configuration file to control the |
behaviour of <application>&dhpackage;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m |
<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable> |
where each element is optional. |
See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details. |
</para> |
<para> |
<segmentedlist> |
<segtitle>Example</segtitle><segtitle>Equivalence</segtitle> |
<segtitle>Standard time format</segtitle> |
<seglistitem> |
<seg>1h30m30</seg><seg>1h30m30s.00</seg><seg>1:30:30.00</seg> |
</seglistitem> |
<seglistitem> |
<seg>30</seg><seg>0h0m30s.00</seg><seg>0:00:30.00</seg> |
</seglistitem> |
<seglistitem> |
<seg>3600</seg><seg>1h0m0s.00</seg><seg>1:00:00.00</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when <filename>/dev/shm</filename> is not avaiable. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>vcs</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and <filename class="devicefile">stderr</filename> can be |
controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&dhpackage;</command> provides some return codes, they follow |
the semi-standardised values defined in <filename>sysexits.h</filename>:</para> |
<segmentedlist> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode>0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>Recent versions of <package>ImageMagick</package>, <command>mplayer</command> and |
<command>ffmpeg</command> should be used |
for maximum compatibility.</para> |
<para>The upstream's <acronym>BTS</acronym> can be found |
at <ulink url="http://b.outlyer.net"/>.</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.3 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.12.2/Makefile |
---|
0,0 → 1,72 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
pkg/vcs.1: manpage.xml |
xmlto -o pkg man $< |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
pkg/vcs.1 \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd pkg && ln -s ../vcs-$(VER).tar.gz ./ |
cd pkg && make PKGBUILD |
$(RM) pkg/vcs-$(VER).tar.gz |
mv pkg/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/vcs |
---|
0,0 → 1,4589 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.12.2" |
declare -r RELEASE=1 |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT=1, --posix or |
#+ --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# * (1.12 or 1.13) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility |
# for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# --end_offset -> --end-offset 1.12 (silent), 1.13 (warn) 1.14 |
# > Usage of ./vcs.conf is also deprecated since it doesn't mesh well with profiles |
# (and it was a bad placeholder for them). Will remain to be loadable with |
# -C:pwd. |
# Loaded by default in 1.12 and maybe 1.13. |
# Not loaded from 1.14 onwards (maybe 1.13), a warning may be shown if the file |
# exists |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# * Optimisations: |
# - Reduce the number of forks |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
declare -r TAB=$'\011' # Tab |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=Black # Font colour for meta info box |
declare fg_sign=Black # Font colour for signature |
declare fg_tstamps=White # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -r DEFAULT_END_OFFSET="5.5%" |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $FONT_MINCHO to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height='100%' |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This amount of time is *not* captured from the end of the video |
declare end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
# TODO: Allow 'y', 'n' in booleans |
# TODO: Remove extra coherence_check()'s once constraints are implemented |
declare -ra OVERRIDE_MAP=( |
"user:::" |
"extended_factor:=:=:f" |
"stdout::" |
"stderr::" |
"DEBUG:=:=:b" |
"interval:=:=:t" |
"numcaps:=:=:p" |
"captures:numcaps:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"columns:cols:=:p" |
"cols:=:alias:p" # Alias |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"bg_heading::" |
"bg_sign::" |
"bg_title::" |
"bg_contact::" |
"bg_tstamps::" |
"fg_heading::" |
"fg_sign::" |
"fg_tstamps::" |
"fg_title::" |
"font_heading::" |
"font_sign::" |
"font_tstamps::" |
"font_title::" |
"font_all:=:meta" # see parse_override |
"bg_all:=:meta" |
"fg_all:=:meta" |
"pts_tstamps::" |
"pts_meta::" |
"pts_sign::" |
"pts_title::" |
# Aliases for cosmetic stuff |
"bg_header:bg_heading:alias" |
"bg_signature:bg_sign:alias" |
"bg_footer:bg_sign:alias" |
"bg_sheet:bg_contact:alias" |
"fg_header:fg_heading:alias" |
"fg_signature:fg_sign:alias" |
"fg_footer:fg_sign:alias" |
"font_header:font_heading:alias" |
"font_meta:font_heading:alias" |
"font_signature:font_sign:alias" |
"font_footer:font_sign:alias" |
"pts_heading:pts_meta:alias" |
"pts_header:pts_meta:alias" |
"pts_signature:pts_sign:alias" |
"pts_footer:pts_sign:alias" |
"signature:user_signature:" |
"user_signature::deprecated=signature" |
"quality:output_quality:=:n" |
"output_quality::deprecated=quality:n" |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"decoder:=:=:D" |
#"capture_mode:timecode_from:alias:T" |
"timecode_from:=:=:T" |
"verbosity:=:=:V" |
"format:output_format:" |
"output_format:=:deprecated=format" |
"simple_feedback:plain_messages:=:b" |
"plain_messages::deprecated=simple_feedback:b" |
"height:th_height:=:h" |
"th_height::deprecated=height:h" |
"padding:HPAD:=:n" |
"HPAD:=:deprecated=padding:n" |
"nonlatin_font:FONT_MINCHO:" |
"FONT_MINCHO::deprecated=nonlatin_font" |
"NONLATIN_FILENAMES:=:=:b" |
"end_offset:=:=:I" # New, used to have a two-variables assignment before USR_* |
"DEFAULT_END_OFFSET:end_offset:deprecated=end_offset:I" |
# TODO TBA: |
#"use_nonlatinfont::" |
#"noboldfeedback::" # Colour but not bold |
"shoehorned::striked" |
"safe_rename_pattern::striked" |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
local cfgfile=$1 |
local desc=$2 |
[ "$desc" ] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
parse_override "$line" # Feeding it comments should be harmless |
por=$RESULT |
if [ "$por" ]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [ "$flag" == '=' ]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[ -z "$ov" ] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [ -z "$ov" -a "$BUFFER" ]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[ -f "$cfgfile" ] || continue |
load_config_file "$cfgfile" |
done |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [ ${p:0:1} == ':' ]; then |
case $p in |
:list) |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[ -f "$path" ] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL"\ |
" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[ -f "$prof" ] || continue |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
local n=$1 v=$2 p=$3 |
# Get constraint |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$n:") |
[ "$map" ] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[ "$ct" ] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
esac |
if [ "$checkfn" ] && ! $checkfn "$v" ; then |
[ "$p" ] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
local o="$1" |
RESULT='' |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=.*' <<<"$o" ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr '[A-Z]' '[a-z]') |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[ "$mapping" ] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[ "$varval" ] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [ "$ivar" ] && [ "$ivar" != "=" ] ; } || ivar="$mvar" |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[ "$varval" ] || return 0 # If empty value, ignore it |
local evcode='' |
if [ "$flags" ] && [ $flags != "=" ] && [ $flags != 'alias' ]; then |
if echo "$flags" | grep -q '^deprecated=' ; then |
local new=$(echo "$flags" | sed 's/^deprecated=//') |
buffered warn "Variable '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Variable '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Variable '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[ -z "$constraints" ] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [ "$curvarval" == "$varval" ]; then |
retflag='=' |
else |
if [ "$constraints" == "t" ]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="$ivar='$varval'; USR_$ivar='$varval'" |
eval "$evcode" |
fi |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
case "$1" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "font_heading=$2" |
parse_override "font_sign=$2" |
parse_override "font_title=$2" |
parse_override "font_tstamps=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "fg_heading=$2" |
parse_override "fg_sign=$2" |
parse_override "fg_tstamps=$2" |
parse_override "fg_title=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "bg_heading=$2" |
parse_override "bg_contact=$2" |
parse_override "bg_sign=$2" |
parse_override "bg_title=$2" |
parse_override "bg_tstamps=$2" |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'decoder=$DEC_FFMPEG' |
cmdline_override() { |
trace $FUNCNAME $@ |
parse_override "$1" |
local r=$RESULT |
[ "$r" ] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [ "$flag" == '=' ]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[ "$arg" != "$cback" ] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $FUNCNAME $@ |
if [ "$CMDLINE_OVERRIDES" ]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [ "$BUFFER" ]; then |
[ "$CMDLINE_OVERRIDES" ] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
#egrep -q '^[0-9]+$' <<<"$1" ; |
# From [[abs]], test if '[ ]' can parse input as numbers |
# Returns 2 for failed test, expected to return 1 |
[ "$1" -ne 0 -o "$1" -eq 0 ] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [ $1 -gt 0 ]; } |
## Bool (0 or 1) |
is_bool() { [ "$1" == "0" -o "$1" == "1" ] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
is_float() { egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" ; } |
## Percentage (xx% or xx.yy%) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[ "$i" ] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [ "$1" -gt 0 ] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" && { |
local d=$(echo "$1" | cut -d'/' -f2) |
[ "$d" -ne 0 ] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [ "$1" == $DEC_FFMPEG -o "$1" == $DEC_MPLAYER ]; } |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [ "$1" == $TC_INTERVAL -o "$1" == $TC_NUMCAPS ]; } |
### Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[ $1 -eq $V_ALL -o $1 -eq $V_NONE -o $1 -eq $V_ERROR -o \ |
$1 -eq $V_WARN -o $1 -eq $V_INFO ] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[A-Z]' '[a-z]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [ "$1" ]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [ "$1" ]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkexf "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully soon this won't be needed |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
# BUFFER=( "${BUFFER[@]}" -- "$grab" ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[ "${BUFFER[*]}" ] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[ "$ec" ] || ec=$ERROR_CODE |
[ "$ec" ] || ec=1 |
[ "$m" ] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if is_percentage $end_offset ; then |
eff_eo=$(percent $end $end_offset) |
else |
eff_eo=$(get_interval "$end_offset") |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [ -z "$USR_end_offset" ] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
inf "Capturing in range [$(pretty_stamp $inc)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
trace $FUNCNAME $@ |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
trace $FUNCNAME $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$stderr" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$stderr" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 0 ] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
if [ -z "$ffl" -a $DVD_MODE -eq 1 ]; then |
ffl="(unavailable in DVD mode)" |
fi |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$extended_factor" -eq 0 ; then |
extended_factor=0 |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ]; then |
# Since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [ $NONLATIN_FILENAMES -eq 1 ] && [ -z "$FONT_MINCHO" ]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
FONT_MINCHO="$font_heading" |
} |
fi |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $FUNCNAME $@ |
# Any default font in use? If all of them are overridden, return |
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [ -d /usr/local/share/fonts ]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [ -z "$dvs" -a -d /usr/share/fonts ]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [ -z "$dvs" ]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[ -z "$USR_font_heading" ] && font_heading="$dvs" |
[ -z "$USR_font_title" ] && font_title="$dvs" |
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs" |
[ -z "$USR_font_sign" ] && font_sign="$dvs" |
[ $DEBUG -eq 1 ] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [ $DVD_MODE -eq 1 ]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [ -f "$dvdn" ]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [ ! -r "$f" ]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [ -z "$DVD_TITLE" -o "$DVD_TITLE" == "0" ]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" && [ "$th_height" != '100%' ]; then |
vidcap_height=$(rpercent ${VID[$H]} ${th_height}) |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkexf "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $extended_factor)") $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
local tlheight=$(line_height "$font_title" "$pts_title") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [ $NONLATIN_FILENAMES -ne 1 ]; then |
fn_font="$font_heading" |
else |
fn_font="$FONT_MINCHO" |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$font_heading" "$pts_meta") |
# Since filename can be set in a different font check it too |
if [ "$fn_font" != "$font_heading" ]; then |
local fnlineheight=$lineheight |
fnlineheight=$(line_height "$fn_font" "$pts_meta") |
[ $fnlineheight -le $lineheight ] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( $lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$font_sign" "$pts_sign") |
local signheight=$(( 4 + ( $signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr '[a-z]' '[A-Z]') f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [ -z "$t" ] || [ "$t" == "=" ]; then t=$f ; fi |
eval v=\$USR_$t |
[ -z "$v" ] || { |
# Symbolic values: |
case "$t" in |
timecode_from) |
x='$TC_NUMCAPS' |
[ $v -eq $TC_NUMCAPS ] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[ $v -eq $DEC_FFMPEG ] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
interval=$(get_interval $2) |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
numcaps=$2 |
timecode_from=$TC_NUMCAPS |
USR_numcaps=$2 |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset|--end-offset) |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [ "$is_p" ]; then |
end_offset="$2" |
else |
end_offset=$(get_interval "$2") |
fi |
USR_end_offset="$end_offset" |
unset is_i |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [ "$2" ]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if echo "$2" | grep -q '^:' ; then |
if [ $2 = ':pwd' ]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[ -f "$cfg" ] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [ $2 != ':pwd' ]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
USR_verbosity=$verbosity |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
error "Shoehorning of options is scheduled to be removed in the next version." |
error " Please contact the author if you absolutely need it." |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [ "$t" -a "$t" != "=" ]; then f="$t" ; fi |
eval v=\$USR_$f |
[ -z "$v" ] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
*) false ;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[ "$1" -o "$POST_GETOPT_HOOKS" ] || { |
[ $verbosity -eq $V_NONE ] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
# Remaining arguments |
if [ ! "$1" ]; then |
[ $verbosity -eq $V_NONE ] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: |& (inherited from csh?) pipes both stdout and stderr |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/common.mk |
---|
0,0 → 1,72 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man/man1/ |
all: |
# Nothing to be done |
dist: vcs.spec |
# Files installed in packages but not outside |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR) |
install -m644 vcs.1 $(DESTDIR)$(MANDIR)/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/vcs.1 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR) |
examples/vcs.conf.example: examples/vcs.conf |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
-$(RM) examples/vcs.conf.example |
distclean: clean |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,113 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
# Manpage |
%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/changelog |
---|
0,0 → 1,75 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman vcs.1 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/docs |
---|
0,0 → 1,0 |
examples/vcs.conf.example |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/profiles/black.conf |
---|
0,0 → 1,9 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/profiles/white.conf |
---|
0,0 → 1,9 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/CHANGELOG |
---|
0,0 → 1,406 |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs -dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,39 |
# |
# $Rev$ |
# |
# Maintainer (Upstream): Toni Corvera <outlyer@gmail.com> |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
build() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/BSDmakefile |
---|
0,0 → 1,14 |
VERSION!=head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/' |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1 |
.endif |
.if empty($(RM)) |
RM=rm -f |
.endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/GNUmakefile |
---|
0,0 → 1,13 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | sed -e 's/.*"\(.*\)".*/\1/') |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/pkg/examples/vcs.conf |
---|
0,0 → 1,152 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
# $Rev$ # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/manpage.xml |
---|
0,0 → 1,382 |
<?xml version='1.0' encoding='UTF-8'?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
This file based on the default template for debian packages. |
Generation of man page: |
$ xmlto man manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections |
read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be |
found in the docbook-xsl-doc-html package. |
Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` |
General documentation about man-pages and man-page-formatting: |
man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ |
--> |
<!-- Fill in your name for FIRSTNAME and SURNAME. --> |
<!ENTITY dhfirstname "Toni"> |
<!ENTITY dhsurname "Corvera"> |
<!-- dhusername could also be set to "&dhfirstname; &dhsurname;". --> |
<!ENTITY dhusername "Toni Corvera"> |
<!ENTITY dhemail "outlyer@gmail.com"> |
<!ENTITY dhsection "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY dhtitle "vcs User Manual"> |
<!ENTITY dhucpackage "VCS"> |
<!ENTITY dhpackage "vcs"> |
]> |
<refentry> |
<refentryinfo> |
<title>&dhtitle;</title> |
<productname>&dhpackage;</productname> |
<authorgroup> |
<author> |
<firstname>&dhfirstname;</firstname> |
<surname>&dhsurname;</surname> |
<contrib>VCS author.</contrib> |
<address> |
<email>&dhemail;</email> |
</address> |
</author> |
</authorgroup> |
<copyright> |
<year>2007-2010</year> |
<holder>&dhusername;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<edition>$Rev$</edition> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&dhucpackage;</refentrytitle> |
<manvolnum>&dhsection;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&dhpackage;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<arg choice="opt"><option>options</option></arg> |
<arg choice="plain"><replaceable>FILE</replaceable></arg> |
<arg choice="opt"><replaceable>FILE</replaceable> |
<arg choice="opt"><replaceable>...</replaceable></arg> |
</arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<arg choice="opt"> |
<option>-n<parameter>20</parameter></option> |
<option>-c<parameter>4</parameter></option> |
</arg> |
<arg choice="plain"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<arg choice="opt"><option>--output=<parameter>OUTPUT1</parameter></option></arg> |
<arg choice="opt"><option>--output=<parameter>OUTPUT2</parameter></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt"><replaceable>INPUT2</replaceable> |
<arg choice="opt"><replaceable>...</replaceable></arg> |
</arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&dhpackage;</command> |
<!-- Normally the help and version options make the programs stop |
right after outputting the requested information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<!-- FIXME --> |
<para>This is a work in progress manual page for <command>&dhpackage;</command>, |
use <parameter>--help</parameter>, <parameter>--fullhelp</parameter> |
and the online documentation (located at <ulink url="http://p.outlyer.net/dox/vcs" />) |
for further usage instructions.</para> |
<para><command>&dhpackage;</command> is a program that creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
</refsect1> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain. Changes the mode of operation |
to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>interval</replaceable></option></term> |
<term><option>--interval=<replaceable>interval</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures. Changes the mode of operation |
to capture at fixed intervals. The number of captures will depend |
on the video length.</para> |
<para>See <xref linkend="interval_format" /> for the allowed syntax.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>number</replaceable></option></term> |
<term><option>--columns=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet. The number of rows |
will depend on this and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>height</replaceable></option></term> |
<term><option>--height=<replaceable>height</replaceable></option></term> |
<listitem> |
<para>Height of captures. Can be a number (of pixels) or a percentage |
(of the video height). By default the same size as the video is used.</para> |
<para>The width is derived from height and aspect ratio.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>filename</replaceable></option></term> |
<term><option>--output=<replaceable>filename</replaceable></option></term> |
<listitem> |
<para>Name of output file. By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> and |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<!-- |
<varlistentry> |
<term><option>-x <replaceable>t</replaceable></option></term> |
<term><option>- -xy=<replaceable>t</replaceable></option></term> |
<listitem> |
<para>DESC.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&dhpackage;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&dhpackage;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${PWD}/vcs.conf</filename></term> |
<listitem> |
<para>The per-directory configuration file to control the |
behaviour of <application>&dhpackage;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m |
<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable> |
where each element is optional. |
See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details. |
</para> |
<para> |
<segmentedlist> |
<segtitle>Example</segtitle><segtitle>Equivalence</segtitle> |
<segtitle>Standard time format</segtitle> |
<seglistitem> |
<seg>1h30m30</seg><seg>1h30m30s.00</seg><seg>1:30:30.00</seg> |
</seglistitem> |
<seglistitem> |
<seg>30</seg><seg>0h0m30s.00</seg><seg>0:00:30.00</seg> |
</seglistitem> |
<seglistitem> |
<seg>3600</seg><seg>1h0m0s.00</seg><seg>1:00:00.00</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when <filename>/dev/shm</filename> is not avaiable. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>vcs</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and <filename class="devicefile">stderr</filename> can be |
controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&dhpackage;</command> provides some return codes, they follow |
the semi-standardised values defined in <filename>sysexits.h</filename>:</para> |
<segmentedlist> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode>0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>Recent versions of <package>ImageMagick</package>, <command>mplayer</command> and |
<command>ffmpeg</command> should be used |
for maximum compatibility.</para> |
<para>The upstream's <acronym>BTS</acronym> can be found |
at <ulink url="http://b.outlyer.net"/>.</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/changelog |
---|
0,0 → 1,69 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/docs |
---|
0,0 → 1,0 |
examples/vcs.conf.example |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/CHANGELOG |
---|
0,0 → 1,395 |
1.12.1: |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (e.g. Debian Lenny) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs -dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/vcs |
---|
0,0 → 1,4544 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.12.1" |
declare -r RELEASE=1 |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT=1, --posix or |
#+ --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# * (1.12 or 1.13) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility |
# for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# --end_offset -> --end-offset 1.12 (silent), 1.13 (warn) 1.14 |
# > Usage of ./vcs.conf is also deprecated since it doesn't mesh well with profiles |
# (and it was a bad placeholder for them). Will remain to be loadable with |
# -C:pwd. |
# Loaded by default in 1.12 and maybe 1.13. |
# Not loaded from 1.14 onwards (maybe 1.13), a warning may be shown if the file |
# exists |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# * Optimisations: |
# - Reduce the number of forks |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename |
declare -ri FF_DEFAULT=5 FF_NONLATIN=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
declare -r TAB=$'\011' # Tab |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=Black # Font colour for meta info box |
declare fg_sign=Black # Font colour for signature |
declare fg_tstamps=White # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -r DEFAULT_END_OFFSET="5.5%" |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height='100%' |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This amount of time is *not* captured from the end of the video |
declare end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
# TODO: Allow 'y', 'n' in booleans |
# TODO: Remove extra coherence_check()'s once constraints are implemented |
declare -ra OVERRIDE_MAP=( |
"user:::" |
"extended_factor:=:=:f" |
"stdout::" |
"stderr::" |
"DEBUG:=:=:b" |
"interval:=:=:t" |
"numcaps:=:=:p" |
"captures:numcaps:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"columns:cols:=:p" |
"cols:=:alias:p" # Alias |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"bg_heading::" |
"bg_sign::" |
"bg_title::" |
"bg_contact::" |
"bg_tstamps::" |
"fg_heading::" |
"fg_sign::" |
"fg_tstamps::" |
"fg_title::" |
"font_heading::" |
"font_sign::" |
"font_tstamps::" |
"font_title::" |
"pts_tstamps::" |
"pts_meta::" |
"pts_sign::" |
"pts_title::" |
# Aliases for cosmetic stuff |
"bg_header:bg_heading:alias" |
"bg_signature:bg_sign:alias" |
"bg_footer:bg_sign:alias" |
"bg_sheet:bg_contact:alias" |
"fg_header:fg_heading:alias" |
"fg_signature:fg_sign:alias" |
"fg_footer:fg_sign:alias" |
"font_header:font_heading:alias" |
"font_meta:font_heading:alias" |
"font_signature:font_sign:alias" |
"font_footer:font_sign:alias" |
"pts_heading:pts_meta:alias" |
"pts_header:pts_meta:alias" |
"pts_signature:pts_sign:alias" |
"pts_footer:pts_sign:alias" |
"signature:user_signature:" |
"user_signature::deprecated=signature" |
"quality:output_quality:=:n" |
"output_quality::deprecated=quality:n" |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"decoder:=:=:D" |
#"capture_mode:timecode_from:alias:T" |
"timecode_from:=:=:T" |
"verbosity:=:=:V" |
"format:output_format:" |
"output_format:=:deprecated=format" |
"simple_feedback:plain_messages:=:b" |
"plain_messages::deprecated=simple_feedback:b" |
"height:th_height:=:h" |
"th_height::deprecated=height:h" |
"padding:HPAD:=:n" |
"HPAD:=:deprecated=padding:n" |
"nonlatin_font:FONT_MINCHO:" |
"FONT_MINCHO::deprecated=nonlatin_font" |
"end_offset:=:=:I" # New, used to have a two-variables assignment before USR_* |
"DEFAULT_END_OFFSET:end_offset:deprecated=end_offset:I" |
# TODO TBA: |
#"use_nonlatinfont::" |
#"noboldfeedback::" # Colour but not bold |
"shoehorned::striked" |
"safe_rename_pattern::striked" |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
local cfgfile=$1 |
local desc=$2 |
[ "$desc" ] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
parse_override "$line" # Feeding it comments should be harmless |
por=$RESULT |
if [ "$por" ]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
bashcode=${tmp#* } |
if [ "$flag" == '=' ]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
eval "$bashcode" |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[ -z "$ov" ] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [ -z "$ov" -a "$BUFFER" ]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[ -f "$cfgfile" ] || continue |
load_config_file "$cfgfile" |
done |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [ ${p:0:1} == ':' ]; then |
case $p in |
:list) |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[ -f "$path" ] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL"\ |
" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[ -f "$prof" ] || continue |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
local n=$1 v=$2 p=$3 |
# Get constraint |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$n:") |
[ "$map" ] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[ "$ct" ] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
esac |
if [ "$checkfn" ] && ! $checkfn "$v" ; then |
[ "$p" ] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override |
# Input should be a var=value assignment, result, stored in the global variable $RESULT, |
# will be in the format: |
# <variable name> <flag> <evaluable code> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# * evaluable code: is a piece of bash code to be feed to eval to change |
# the variable, it also sets the related USR_* variable |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
local o="$1" |
RESULT='' |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=.*' <<<"$o" ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr '[A-Z]' '[a-z]') |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[ "$mapping" ] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[ "$varval" ] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [ "$ivar" ] && [ "$ivar" != "=" ] ; } || ivar="$mvar" |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[ "$varval" ] || return 0 # If empty value, ignore it |
local evcode='' |
if [ "$flags" ] && [ $flags != "=" ] && [ $flags != 'alias' ]; then |
if echo "$flags" | grep -q '^deprecated=' ; then |
local new=$(echo "$flags" | sed 's/^deprecated=//') |
buffered warn "Variable '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Variable '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Variable '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[ -z "$constraints" ] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [ "$curvarval" == "$varval" ]; then |
retflag='=' |
else |
if [ "$constraints" == "t" ]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="$ivar='$varval'; USR_$ivar='$varval'" |
fi |
# varname, as found in the config file |
RESULT="$varname $retflag $evcode" |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'decoder=$DEC_FFMPEG' |
cmdline_override() { |
trace $FUNCNAME $@ |
parse_override "$1" |
local r=$RESULT |
[ "$r" ] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
local bashcode=${tmp#* } |
if [ "$flag" == '=' ]; then |
varname="$varname(=)" |
else |
eval "$bashcode" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[ "$arg" != "$cback" ] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $FUNCNAME $@ |
if [ "$CMDLINE_OVERRIDES" ]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [ "$BUFFER" ]; then |
[ "$CMDLINE_OVERRIDES" ] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
#egrep -q '^[0-9]+$' <<<"$1" ; |
# From [[abs]], test if '[ ]' can parse input as numbers |
# Returns 2 for failed test, expected to return 1 |
[ "$1" -ne 0 -o "$1" -eq 0 ] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [ $1 -gt 0 ]; } |
## Bool (0 or 1) |
is_bool() { [ "$1" == "0" -o "$1" == "1" ] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
is_float() { egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" ; } |
## Percentage (xx% or xx.yy%) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[ "$i" ] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [ "$1" -gt 0 ] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" && { |
local d=$(echo "$1" | cut -d'/' -f2) |
[ "$d" -ne 0 ] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [ "$1" == $DEC_FFMPEG -o "$1" == $DEC_MPLAYER ]; } |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [ "$1" == $TC_INTERVAL -o "$1" == $TC_NUMCAPS ]; } |
### Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[ $1 -eq $V_ALL -o $1 -eq $V_NONE -o $1 -eq $V_ERROR -o \ |
$1 -eq $V_WARN -o $1 -eq $V_INFO ] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[A-Z]' '[a-z]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [ "$1" ]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [ "$1" ]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkexf "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully soon this won't be needed |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[*]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
# BUFFER=( "${BUFFER[@]}" -- "$grab" ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[ "${BUFFER[*]}" ] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[ "$ec" ] || ec=$ERROR_CODE |
[ "$ec" ] || ec=1 |
[ "$m" ] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if is_percentage $end_offset ; then |
eff_eo=$(percent $end $end_offset) |
else |
eff_eo=$(get_interval "$end_offset") |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [ -z "$USR_end_offset" ] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
inf "Capturing in range [$(pretty_stamp $inc)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
trace $FUNCNAME $@ |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
trace $FUNCNAME $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$stderr" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$stderr" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 0 ] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
if [ -z "$ffl" -a $DVD_MODE -eq 1 ]; then |
ffl="(unavailable in DVD mode)" |
fi |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$extended_factor" -eq 0 ; then |
extended_factor=0 |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ]; then |
# Since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $FUNCNAME $@ |
# Any default font in use? If all of them are overridden, return |
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [ -d /usr/local/share/fonts ]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [ -z "$dvs" -a -d /usr/share/fonts ]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [ -z "$dvs" ]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[ -z "$USR_font_heading" ] && font_heading="$dvs" |
[ -z "$USR_font_title" ] && font_title="$dvs" |
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs" |
[ -z "$USR_font_sign" ] && font_sign="$dvs" |
[ $DEBUG -eq 1 ] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [ $DVD_MODE -eq 1 ]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [ -f "$dvdn" ]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [ ! -r "$f" ]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [ -z "$DVD_TITLE" -o "$DVD_TITLE" == "0" ]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" && [ "$th_height" != '100%' ]; then |
vidcap_height=$(rpercent ${VID[$H]} ${th_height}) |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkexf "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $extended_factor)") $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
local tlheight=$(line_height "$font_title" "$pts_title") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_NONLATIN) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$font_heading" "$pts_meta") |
# Since filename can be set in a different font check it too |
if [ "$fn_font" != "$font_heading" ]; then |
local fnlineheight=$lineheight |
fnlineheight=$(line_height "$fn_font" "$pts_meta") |
[ $fnlineheight -le $lineheight ] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( $lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$font_sign" "$pts_sign") |
local signheight=$(( 4 + ( $signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr '[a-z]' '[A-Z]') f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [ -z "$t" ] || [ "$t" == "=" ]; then t=$f ; fi |
eval v=\$USR_$t |
[ -z "$v" ] || { |
# Symbolic values: |
case "$t" in |
timecode_from) |
x='$TC_NUMCAPS' |
[ $v -eq $TC_NUMCAPS ] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[ $v -eq $DEC_FFMPEG ] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
echo "$f=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
interval=$(get_interval $2) |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
numcaps=$2 |
timecode_from=$TC_NUMCAPS |
USR_numcaps=$2 |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset|--end-offset) |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [ "$is_p" ]; then |
end_offset="$2" |
else |
end_offset=$(get_interval "$2") |
fi |
USR_end_offset="$end_offset" |
unset is_i |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [ "$2" ]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
font_filename=$FF_NONLATIN |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_NONLATIN |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if echo "$2" | grep -q '^:' ; then |
if [ $2 = ':pwd' ]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[ -f "$cfg" ] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [ $2 != ':pwd' ]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
USR_verbosity=$verbosity |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
error "Shoehorning of options is scheduled to be removed in the next version." |
error " Please contact the author if you absolutely need it." |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [ "$t" -a "$t" != "=" ]; then f="$t" ; fi |
eval v=\$USR_$f |
[ -z "$v" ] || echo "$f=$v" |
done |
exit 0 |
;; |
*) false ;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[ "$1" -o "$POST_GETOPT_HOOKS" ] || { |
[ $verbosity -eq $V_NONE ] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
# Remaining arguments |
if [ ! "$1" ]; then |
[ $verbosity -eq $V_NONE ] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: |& (inherited from csh?) pipes both stdout and stderr |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,39 |
# |
# $Rev$ |
# |
# Maintainer (Upstream): Toni Corvera <outlyer@gmail.com> |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
build() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/profiles/mosaic.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/profiles/black.conf |
---|
0,0 → 1,8 |
# vcs:conf: |
# vcs:desc: White-on-Black |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/profiles/white.conf |
---|
0,0 → 1,8 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
bg_contact=White |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/BSDmakefile |
---|
0,0 → 1,14 |
VERSION!=head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/' |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1 |
.endif |
.if empty($(RM)) |
RM=rm -f |
.endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/GNUmakefile |
---|
0,0 → 1,13 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | sed -e 's/.*"\(.*\)".*/\1/') |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,110 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/common.mk |
---|
0,0 → 1,66 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
all: |
# Nothing to be done |
dist: vcs.spec |
# Files installed in packages but not outside |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
examples/vcs.conf.example: examples/vcs.conf |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
-$(RM) examples/vcs.conf.example |
distclean: clean |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/pkg/examples/vcs.conf |
---|
0,0 → 1,152 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
# $Rev$ # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/Makefile |
---|
0,0 → 1,68 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd pkg && ln -s ../vcs-$(VER).tar.gz ./ |
cd pkg && make PKGBUILD |
$(RM) pkg/vcs-$(VER).tar.gz |
mv pkg/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12.1 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/changelog |
---|
0,0 → 1,63 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/docs |
---|
0,0 → 1,0 |
examples/vcs.conf.example |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.12/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.12/pkg/profiles/mosaic.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/profiles/black.conf |
---|
0,0 → 1,8 |
# vcs:conf: |
# vcs:desc: White-on-Black |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/profiles/white.conf |
---|
0,0 → 1,8 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
bg_contact=White |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/BSDmakefile |
---|
0,0 → 1,14 |
VERSION!=head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/' |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1 |
.endif |
.if empty($(RM)) |
RM=rm -f |
.endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/CHANGELOG |
---|
0,0 → 1,389 |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs -dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/GNUmakefile |
---|
0,0 → 1,13 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | sed -e 's/.*"\(.*\)".*/\1/') |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/vcs |
---|
0,0 → 1,4534 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.12" |
declare -r RELEASE=1 |
set -e |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# * (1.12 or 1.13) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility |
# for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# --end_offset -> --end-offset 1.12 (silent), 1.13 (warn) 1.14 |
# > Usage of ./vcs.conf is also deprecated since it doesn't mesh well with profiles |
# (and it was a bad placeholder for them). Will remain to be loadable with |
# -C:pwd. |
# Loaded by default in 1.12 and maybe 1.13. |
# Not loaded from 1.14 onwards (maybe 1.13), a warning may be shown if the file |
# exists |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# * Optimisations: |
# - Reduce the number of forks |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename |
declare -ri FF_DEFAULT=5 FF_NONLATIN=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
declare -r TAB=$'\011' # Tab |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=Black # Font colour for meta info box |
declare fg_sign=Black # Font colour for signature |
declare fg_tstamps=White # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -r DEFAULT_END_OFFSET="5.5%" |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height='100%' |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This amount of time is *not* captured from the end of the video |
declare end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
# TODO: Allow 'y', 'n' in booleans |
# TODO: Remove extra coherence_check()'s once constraints are implemented |
declare -ra OVERRIDE_MAP=( |
"user:::" |
"extended_factor:=:=:f" |
"stdout::" |
"stderr::" |
"DEBUG:=:=:b" |
"interval:=:=:t" |
"numcaps:=:=:p" |
"captures:numcaps:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"columns:cols:=:p" |
"cols:=:alias:p" # Alias |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"bg_heading::" |
"bg_sign::" |
"bg_title::" |
"bg_contact::" |
"bg_tstamps::" |
"fg_heading::" |
"fg_sign::" |
"fg_tstamps::" |
"fg_title::" |
"font_heading::" |
"font_sign::" |
"font_tstamps::" |
"font_title::" |
"pts_tstamps::" |
"pts_meta::" |
"pts_sign::" |
"pts_title::" |
# Aliases for cosmetic stuff |
"bg_header:bg_heading:alias" |
"bg_signature:bg_sign:alias" |
"bg_footer:bg_sign:alias" |
"bg_sheet:bg_contact:alias" |
"fg_header:fg_heading:alias" |
"fg_signature:fg_sign:alias" |
"fg_footer:fg_sign:alias" |
"font_header:font_heading:alias" |
"font_meta:font_heading:alias" |
"font_signature:font_sign:alias" |
"font_footer:font_sign:alias" |
"pts_heading:pts_meta:alias" |
"pts_header:pts_meta:alias" |
"pts_signature:pts_sign:alias" |
"pts_footer:pts_sign:alias" |
"signature:user_signature:" |
"user_signature::deprecated=signature" |
"quality:output_quality:=:n" |
"output_quality::deprecated=quality:n" |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"decoder:=:=:D" |
#"capture_mode:timecode_from:alias:T" |
"timecode_from:=:=:T" |
"verbosity:=:=:V" |
"format:output_format:" |
"output_format:=:deprecated=format" |
"simple_feedback:plain_messages:=:b" |
"plain_messages::deprecated=simple_feedback:b" |
"height:th_height:=:h" |
"th_height::deprecated=height:h" |
"padding:HPAD:=:n" |
"HPAD:=:deprecated=padding:n" |
"nonlatin_font:FONT_MINCHO:" |
"FONT_MINCHO::deprecated=nonlatin_font" |
"end_offset:=:=:I" # New, used to have a two-variables assignment before USR_* |
"DEFAULT_END_OFFSET:end_offset:deprecated=end_offset:I" |
# TODO TBA: |
#"use_nonlatinfont::" |
#"noboldfeedback::" # Colour but not bold |
"shoehorned::striked" |
"safe_rename_pattern::striked" |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
local cfgfile=$1 |
local desc=$2 |
[ "$desc" ] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
parse_override "$line" # Feeding it comments should be harmless |
por=$RESULT |
if [ "$por" ]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
bashcode=${tmp#* } |
if [ "$flag" == '=' ]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
eval "$bashcode" |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[ -z "$ov" ] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [ -z "$ov" -a "$BUFFER" ]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[ -f "$cfgfile" ] || continue |
load_config_file "$cfgfile" |
done |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [ ${p:0:1} == ':' ]; then |
case $p in |
:list) |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[ -f "$path" ] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL"\ |
" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[ -f "$prof" ] || continue |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
local n=$1 v=$2 p=$3 |
# Get constraint |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$n:") |
[ "$map" ] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[ "$ct" ] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
esac |
if [ "$checkfn" ] && ! $checkfn "$v" ; then |
[ "$p" ] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override |
# Input should be a var=value assignment, result, stored in the global variable $RESULT, |
# will be in the format: |
# <variable name> <flag> <evaluable code> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# * evaluable code: is a piece of bash code to be feed to eval to change |
# the variable, it also sets the related USR_* variable |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
local o="$1" |
RESULT='' |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*=.*' <<<"$o" ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr '[A-Z]' '[a-z]') |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[ "$mapping" ] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[ "$varval" ] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [ "$ivar" ] && [ "$ivar" != "=" ] ; } || ivar="$mvar" |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[ "$varval" ] || return 0 # If empty value, ignore it |
local evcode='' |
if [ "$flags" ] && [ $flags != "=" ] && [ $flags != 'alias' ]; then |
if echo "$flags" | grep -q '^deprecated=' ; then |
local new=$(echo "$flags" | sed 's/^deprecated=//') |
buffered warn "Variable '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Variable '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Variable '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[ -z "$constraints" ] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [ "$curvarval" == "$varval" ]; then |
retflag='=' |
else |
if [ "$constraints" == "t" ]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="$ivar='$varval'; USR_$ivar='$varval'" |
fi |
# varname, as found in the config file |
RESULT="$varname $retflag $evcode" |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'decoder=$DEC_FFMPEG' |
cmdline_override() { |
trace $FUNCNAME $@ |
parse_override "$1" |
local r=$RESULT |
[ "$r" ] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
local bashcode=${tmp#* } |
if [ "$flag" == '=' ]; then |
varname="$varname(=)" |
else |
eval "$bashcode" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[ "$arg" != "$cback" ] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $FUNCNAME $@ |
if [ "$CMDLINE_OVERRIDES" ]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [ "$BUFFER" ]; then |
[ "$CMDLINE_OVERRIDES" ] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
#egrep -q '^[0-9]+$' <<<"$1" ; |
# From [[abs]], test if '[ ]' can parse input as numbers |
# Returns 2 for failed test, expected to return 1 |
[ "$1" -ne 0 -o "$1" -eq 0 ] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [ $1 -gt 0 ]; } |
## Bool (0 or 1) |
is_bool() { [ "$1" == "0" -o "$1" == "1" ] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
is_float() { egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" ; } |
## Percentage (xx% or xx.yy%) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[ "$i" ] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [ "$1" -gt 0 ] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" && { |
local d=$(echo "$1" | cut -d'/' -f2) |
[ "$d" -ne 0 ] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [ "$1" == $DEC_FFMPEG -o "$1" == $DEC_MPLAYER ]; } |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [ "$1" == $TC_INTERVAL -o "$1" == $TC_NUMCAPS ]; } |
### Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[ $1 -eq $V_ALL -o $1 -eq $V_NONE -o $1 -eq $V_ERROR -o \ |
$1 -eq $V_WARN -o $1 -eq $V_INFO ] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[A-Z]' '[a-z]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [ "$1" ]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [ "$1" ]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkexf "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully soon this won't be needed |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[*]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
# BUFFER=( "${BUFFER[@]}" -- "$grab" ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[ "${BUFFER[*]}" ] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[ "$ec" ] || ec=$ERROR_CODE |
[ "$ec" ] || ec=1 |
[ "$m" ] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi >/dev/null |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if is_percentage $end_offset ; then |
eff_eo=$(percent $end $end_offset) |
else |
eff_eo=$(get_interval "$end_offset") |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [ -z "$USR_end_offset" ] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
inf "Capturing in range [$(pretty_stamp $inc)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
trace $FUNCNAME $@ |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
trace $FUNCNAME $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$stderr" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$stderr" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 0 ] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
if [ -z "$ffl" -a $DVD_MODE -eq 1 ]; then |
ffl="(unavailable in DVD mode)" |
fi |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$extended_factor" -eq 0 ; then |
extended_factor=0 |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ]; then |
# Since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $FUNCNAME $@ |
# Any default font in use? If all of them are overridden, return |
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [ -d /usr/local/share/fonts ]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [ -z "$dvs" -a -d /usr/share/fonts ]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [ -z "$dvs" ]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[ -z "$USR_font_heading" ] && font_heading="$dvs" |
[ -z "$USR_font_title" ] && font_title="$dvs" |
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs" |
[ -z "$USR_font_sign" ] && font_sign="$dvs" |
[ $DEBUG -eq 1 ] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [ $DVD_MODE -eq 1 ]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [ -f "$dvdn" ]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
elif [ ! -r "$dvdn" ]; then # Not an ISO, is it readable? |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [ -z "$DVD_TITLE" -o "$DVD_TITLE" == "0" ]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" && [ "$th_height" != '100%' ]; then |
vidcap_height=$(rpercent ${VID[$H]} ${th_height}) |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkexf "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $extended_factor)") $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
local tlheight=$(line_height "$font_title" "$pts_title") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_NONLATIN) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$font_heading" "$pts_meta") |
# Since filename can be set in a different font check it too |
if [ "$fn_font" != "$font_heading" ]; then |
local fnlineheight=$lineheight |
fnlineheight=$(line_height "$fn_font" "$pts_meta") |
[ $fnlineheight -le $lineheight ] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( $lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$font_sign" "$pts_sign") |
local signheight=$(( 4 + ( $signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr '[a-z]' '[A-Z]') f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [ -z "$t" ] || [ "$t" == "=" ]; then t=$f ; fi |
eval v=\$USR_$t |
[ -z "$v" ] || { |
# Symbolic values: |
case "$t" in |
timecode_from) |
x='$TC_NUMCAPS' |
[ $v -eq $TC_NUMCAPS ] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[ $v -eq $DEC_FFMPEG ] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
echo "$f=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
interval=$(get_interval $2) |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
numcaps=$2 |
timecode_from=$TC_NUMCAPS |
USR_numcaps=$2 |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset|--end-offset) |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [ "$is_p" ]; then |
end_offset="$2" |
else |
end_offset=$(get_interval "$2") |
fi |
USR_end_offset="$end_offset" |
unset is_i |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [ "$2" ]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
font_filename=$FF_NONLATIN |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_NONLATIN |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if echo "$2" | grep -q '^:' ; then |
if [ $2 = ':pwd' ]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[ -f "$cfg" ] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [ $2 != ':pwd' ]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
USR_verbosity=$verbosity |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
error "Shoehorning of options is scheduled to be removed in the next version." |
error " Please contact the author if you absolutely need it." |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [ "$t" -a "$t" != "=" ]; then f="$t" ; fi |
eval v=\$USR_$f |
[ -z "$v" ] || echo "$f=$v" |
done |
exit 0 |
;; |
*) false ;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[ "$1" -o "$POST_GETOPT_HOOKS" ] || { |
[ $verbosity -eq $V_NONE ] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
# Remaining arguments |
if [ ! "$1" ]; then |
[ $verbosity -eq $V_NONE ] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: |& (inherited from csh?) pipes both stdout and stderr |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,39 |
# |
# $Rev$ |
# |
# Maintainer (Upstream): Toni Corvera <outlyer@gmail.com> |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
build() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,110 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/common.mk |
---|
0,0 → 1,66 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
all: |
# Nothing to be done |
dist: vcs.spec |
# Files installed in packages but not outside |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
examples/vcs.conf.example: examples/vcs.conf |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
-$(RM) examples/vcs.conf.example |
distclean: clean |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/pkg/examples/vcs.conf |
---|
0,0 → 1,152 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
# $Rev$ # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/Makefile |
---|
0,0 → 1,68 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd pkg && ln -s ../vcs-$(VER).tar.gz ./ |
cd pkg && make PKGBUILD |
$(RM) pkg/vcs-$(VER).tar.gz |
mv pkg/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.12 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.11.2/Makefile |
---|
0,0 → 1,59 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/vcs |
---|
0,0 → 1,4091 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
declare -r VERSION="1.11.2" |
declare -r RELEASE=1 |
# {{{ # CHANGELOG |
# Last release changes: |
# (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.11.2: |
# * Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
# * BUGFIXES: |
# - Remove extra, empty, temporary dir |
# - Use standard awk syntax for exponentiation (pyth_th) |
# - Workaround for systems that don't register fonts with ImageMagick |
# * DEBUG: Print to stderr when probbing with mplayer too |
# 1.11.1: |
# * Added FLV1 codec |
# * BUGFIXES: |
# - Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
# overrides, warn about their new names (interval, numcaps and cols) |
# - Fix ImageMagick version detection |
# 1.11: |
# * FEATURES |
# - Allow setting output filename. With extension will set output format, |
# without will inherit it. |
# - Allow percentages in height. |
# - Require mplayer OR ffmpeg instead of both. Having both is still |
# recommended for better results. |
# - Safe mode, for files whose length doesn't get reported correctly. |
# Completely automated. |
# Number of tries can be increased with -Ws. Repeat to increase further. |
# Use -WS to do try as many times as possible. |
# Accuracy (stepping) can be increased with -Wp. Repeat to increase |
# accuracy. Decrease with -WP. |
# Can be deliberately disabled with -Wb to force processing of broken |
# files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
# - Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
# * BUGFIXES: |
# - Don't pass ms to mplayer. It ignores them anyway and in some rare |
# cases breaks the last capture (possibly due to the 5-frames hack) |
# - Honor detected aspect ratio if found |
# - Try to detect files that might fail on the last capture and trigger |
# safe mode |
# - Timestamps font was being ignored. As a side effect this produced |
# italiced timestamps in some systems |
# - Fixed obscure bug with safe_rename_pattern overrides |
# * COMPAT: Support for bash 2.05b. This will (probably) be the last version |
# capable of running under bash 2. |
# * DVD mode revamp |
# - Print title file size instead of disc size when possible |
# - Aspect ratio detection, if available |
# - Use of FFmpeg if available to get better information |
# - Mostly x-platform, only ISOs identification is a bit better in Linux |
# * Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
# (MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
# Video codec renamings: |
# - TechSmith codec name shortened to TechSmith SCC |
# - Raw RGB renamed to Raw video |
# * Help cleanup. The default help output is much shorter, the full text |
# can be displayed with --fullhelp. Also print the decoder choice near |
# the appropriate option (-M/-F) |
# * Added --anonymous to help (never was in it) |
# * Drop requirement on seq/jot and bc, replaced by inline awk |
# ... New requirement: Perl (only for DVDs). |
# * Adopt new/fixed numbering scheme |
# <http://p.outlyer.net/dox/vcs:devel:renumbering> |
# * Check ImageMagick version (must decide which is the real minimum |
# required) |
# * Non-latin fonts revamp: |
# - -I no longer works alone (use -Ij or -Ik instead) |
# - -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
# - -I accepts a font name or font filename like |
# -Ij=Kochi-Mincho-Regular or |
# -Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# * Deprecated options: |
# --shoehorn: Will be removed unless someone really needs it. |
# --mincho: Replaced by --nonlatin |
# * COSMETIC: |
# - Default font switched to DejaVu Sans. |
# Font sizes reduced to accomodate the new default. |
# Should fall back to a sane default if it's not available |
# - Much tighter padding |
# - Smaller timestamps font by default |
# - Print friendlier timestamp when a capture fails |
# - Print program signature to console without colour |
# - Use main font by default in timestamps |
# - Heading background colour toned down |
# - Added colourised output when tput is not capable (i.e. FreeBSD) |
# - Added prefixes when colour is not available for console output |
# - Don't print lsdvd error channel is DVD mode |
# - Suppress mv errors (e.g. over VFS being unable to preserve) |
# * Minimum ImageMagick version set to 6.3.5-7 |
# * Better detection of requirements (e.g. disallow decoders without png |
# support) |
# * Allow overriding height, number of captures, interval, columns, and |
# padding |
# * UNDOCUMENTED/DEBUG: |
# - Allow stopping the main loop before cleaning up (--undocumented hang) |
# - Identification-only mode. Might be promoted to an actual feature |
# (--undocumented idonly) |
# - Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
# set_mplayer) |
# - Allow disabling either mplayer of ffmpeg (as if they weren't |
# installed (--undocumented disable_ffmpeg and disable_mplayer) |
# - Added -Wc to disable console colour, repeat to disable prefixes |
# * INTERNAL: |
# - assert() |
# - Cleanup: correctness checks converted to asserts, removal of old dead |
# code |
# - Typos |
# |
# }}} # CHANGELOG |
set -e |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# TODO / FIXME: |
# * (1.12) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --shoehorn -> --undocumented shoehorn 1.11 1.12 |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# MIN_LENGTH_FOR_END_OFFSET -> none 1.11 1.12 or 1.13 |
# * No warning shown in 1.11. 1.12 will switch back default end offset either to 0 or a |
# percentage *and* use it always (disable with -E0) |
# end offset -> none ? ?+1 |
# --mincho -> --nonlatin 1.11 1.12 |
# * Better check for coherence of overrides |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height= # *WILL CHANGE NAME* |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
'th_height' |
'interval' |
'numcaps' |
'HPAD' |
'cols' |
# Note GETOPT doesn't make sense to be overridden from the command-line |
'GETOPT' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
end_offset=$DEFAULT_END_OFFSET |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=${ALLOWED_OVERRIDES[*]} |
compregex=${compregex// /|} # = s ' ' => '|' |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really work anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(egrep -o '^[[:space:]]*[a-zA-Z0-9_]*=.' <<<"$o" | cut -d'=' -f1 | egrep -o '[^ ]*') |
local varval=$(egrep -o '^[^=]*=.*' <<<"$o" | cut -d'=' -f2-) |
if [ "$varname" = "DEFAULT_INTERVAL" ]; then |
warn '$DEFAULT_INTERVAL is deprecated, please use $interval instead' |
varname=interval |
elif [ "$varname" = "DEFAULT_NUMCAPS" ]; then |
warn '$DEFAULT_NUMCAPS is deprecated, please use $numcaps instead' |
varname=numcaps |
elif [ "$varname" = "DEFAULT_COLS" ]; then |
warn '$DEFAULT_COLS is deprecated, please use $cols instead' |
varname=cols |
fi |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
eval "USR_$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input is a valid percentage (xx% or xx.yy%) |
# is_percentage($1 = input) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the all code (default bc -l) |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl($1 = string) |
stonl() { |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
} |
# Converts newlines to spaces portably |
# nltos($1 = string) |
nltos() { |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkex "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkex "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkex "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully sson this won't be needed |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$DVD_FILE" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[*]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 >/dev/null && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$(awkex "$end - $st") |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
# Note mplayer CAN'T set the output filename |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$DVD_FILE" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkex "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$stderr" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>"$stderr" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
# (AFAIK) Can't use ffmpeg in DVD Mode |
#[ $DVD_MODE -eq 0 ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locale mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed 's/:/h/' | sed 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkex "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
[ -z "$ffl" ] && [ $DVD_MODE -eq 1 ] && ffl="(unavailable in DVD mode)" |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:" |
inf "$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ] ; then |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Override-able options check, in case they were set from overrides instead |
#+of equivalent command-line options. Will check the actual selected values, |
#+i.e. fail silently if the overrides aren't effective |
[ "$USR_th_height" ] && { check_height "$th_height" || exit $? ; } |
[ "$USR_numcaps" ] && { check_numcaps "$numcaps" || exit $? ; } |
[ "$USR_interval" ] && { check_interval "$interval" || exit $? ; } |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
# Any default font in use? If all of them are overridden, return |
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return |
# Try to locate DejaVu Sans |
[ ! -d /usr/share/fonts ] && return |
local dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
if [ -z "$dvs" ]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[ -z "$USR_font_heading" ] && font_heading="$dvs" |
[ -z "$USR_font_title" ] && font_title="$dvs" |
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs" |
[ -z "$USR_font_sign" ] && font_sign="$dvs" |
[ $DEBUG -eq 1 ] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
check_height() { # Acceptable height |
if ! is_number "$1" && ! is_percentage "$1" ; then |
error "Height must be a (positive) number or a percentage. Got '$1'." |
return $EX_USAGE |
fi |
} |
check_numcaps() { # Acceptable numcaps |
if ! is_number "$1" ; then |
error "Number of captures must be a (positive) a number! Got '$1'." |
return $EX_USAGE |
fi |
if [ $1 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$1'." |
return $EX_USAGE |
fi |
} |
check_interval() { # Acceptable interval |
if ! get_interval "$1" >/dev/null ; then |
error "Incorrect interval format. Got '$1'." |
return $EX_USAGE |
fi |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
DVD_DEVICE= |
local dvdn=$(realpathr "$f") # dvdn might be a device or an ISO |
if [ -f "$dvdn" ]; then |
# It's an ISO |
DVD_MOUNTP=$(get_dvd_image_mountpoint) |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
elif [ ! -r "$dvdn" ]; then |
# It's something else we cannot read |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_DEVICE="$dvdn" |
DVD_MOUNTP=$(mount | grep -o "^$DVD_DEVICE *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD" |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$DVD_FILE" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
unset dt |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS)" |
fi |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" ; then |
local pc=${th_height/%%/} # BASH %% == RE %$ |
vidcap_height=$(awkex "int ((${VID[$H]} * ${pc}) / 100 + 0.5)") |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkex "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkex "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$DVD_FILE" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# Need a mountpoint to get the actual *title* size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Title size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.3576 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:V:R:Z:o:P: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:,randomsource:,undocumented:,output:,fullhelp,profile:,"\ |
"jpeg2,nonlatin" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_interval "$2" |
interval=$(get_interval "$2") |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_numcaps "$2" |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
USR_numcaps="$2" |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_end_offset="$end_offset" |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
--shoehorn) |
warn "$1 is deprecated, please use '--undocumented shoehorn=\"$2\"' instead" |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_height "$2" |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
--mincho) |
warn "--mincho is deprecated, please use -Ij or --nonlatin instead" |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
override "$2" "command line" |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkex "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-P|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
esac |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
;; |
*) false ;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/Makefile |
---|
0,0 → 1,44 |
# $Id$ |
prefix:=/usr/local |
DESTDIR:=/ |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/') |
PACKAGER:=$(shell echo $$DEBFULLNAME) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
endif |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
all: |
# Nothing to be done |
dist: vcs.spec PKGBUILD |
install: |
install -D -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
-rmdir -p $(DESTDIR)$(prefix)/bin |
vcs.spec: rpm/vcs.spec.in |
test -f vcs -a "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
PKGBUILD: arch/PKGBUILD.in |
test -f vcs -a "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e 's!@MD5@!'\''$(shell bzip2 -c9 < vcs | md5sum -b - | cut -d' ' -f1)'\''!g' \ |
-e 's!@SHA1@!'\''$(shell bzip2 -c9 < vcs | sha1sum -b - | cut -d' ' -f1)'\''!g' > $@ |
clean: |
distclean: |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,97 |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
%install |
make DESTDIR=%buildroot prefix=/usr install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
%changelog |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/debian/changelog |
---|
0,0 → 1,56 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 12 Mar 2010 01:36:10 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/CHANGELOG |
---|
0,0 → 1,320 |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,28 |
# Maintainer: Toni Corvera <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
source=($url/files/$pkgname-$pkgver.bz2) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch don't agree on this (???) sha256sums=() |
build() { |
# cd $srcdir/$pkgname-$pkgver |
# bzip2 -dc $pkgname-$pkgver.bz2 > $pkgname-$pkgver |
install -D -m755 $pkgname-$pkgver ${pkgdir}/usr/bin/$pkgname || return 1 |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
/ATTIC/video-contact-sheet/tags/1.11.2/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/vcs |
---|
0,0 → 1,4088 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
declare -r VERSION="1.11.2" |
declare -r RELEASE=1 |
# {{{ # CHANGELOG |
# Last release changes: |
# (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.11.2: |
# * BUGFIXES: |
# - Remove extra, empty, temporary dir |
# - Use standard awk syntax for exponentiation (pyth_th) |
# - Workaround for systems that don't register fonts with ImageMagick |
# * DEBUG: Print to stderr when probbing with mplayer too |
# 1.11.1: |
# * Added FLV1 codec |
# * BUGFIXES: |
# - Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
# overrides, warn about their new names (interval, numcaps and cols) |
# - Fix ImageMagick version detection |
# 1.11: |
# * FEATURES |
# - Allow setting output filename. With extension will set output format, |
# without will inherit it. |
# - Allow percentages in height. |
# - Require mplayer OR ffmpeg instead of both. Having both is still |
# recommended for better results. |
# - Safe mode, for files whose length doesn't get reported correctly. |
# Completely automated. |
# Number of tries can be increased with -Ws. Repeat to increase further. |
# Use -WS to do try as many times as possible. |
# Accuracy (stepping) can be increased with -Wp. Repeat to increase |
# accuracy. Decrease with -WP. |
# Can be deliberately disabled with -Wb to force processing of broken |
# files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
# - Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
# * BUGFIXES: |
# - Don't pass ms to mplayer. It ignores them anyway and in some rare |
# cases breaks the last capture (possibly due to the 5-frames hack) |
# - Honor detected aspect ratio if found |
# - Try to detect files that might fail on the last capture and trigger |
# safe mode |
# - Timestamps font was being ignored. As a side effect this produced |
# italiced timestamps in some systems |
# - Fixed obscure bug with safe_rename_pattern overrides |
# * COMPAT: Support for bash 2.05b. This will (probably) be the last version |
# capable of running under bash 2. |
# * DVD mode revamp |
# - Print title file size instead of disc size when possible |
# - Aspect ratio detection, if available |
# - Use of FFmpeg if available to get better information |
# - Mostly x-platform, only ISOs identification is a bit better in Linux |
# * Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
# (MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
# Video codec renamings: |
# - TechSmith codec name shortened to TechSmith SCC |
# - Raw RGB renamed to Raw video |
# * Help cleanup. The default help output is much shorter, the full text |
# can be displayed with --fullhelp. Also print the decoder choice near |
# the appropriate option (-M/-F) |
# * Added --anonymous to help (never was in it) |
# * Drop requirement on seq/jot and bc, replaced by inline awk |
# ... New requirement: Perl (only for DVDs). |
# * Adopt new/fixed numbering scheme |
# <http://p.outlyer.net/dox/vcs:devel:renumbering> |
# * Check ImageMagick version (must decide which is the real minimum |
# required) |
# * Non-latin fonts revamp: |
# - -I no longer works alone (use -Ij or -Ik instead) |
# - -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
# - -I accepts a font name or font filename like |
# -Ij=Kochi-Mincho-Regular or |
# -Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# * Deprecated options: |
# --shoehorn: Will be removed unless someone really needs it. |
# --mincho: Replaced by --nonlatin |
# * COSMETIC: |
# - Default font switched to DejaVu Sans. |
# Font sizes reduced to accomodate the new default. |
# Should fall back to a sane default if it's not available |
# - Much tighter padding |
# - Smaller timestamps font by default |
# - Print friendlier timestamp when a capture fails |
# - Print program signature to console without colour |
# - Use main font by default in timestamps |
# - Heading background colour toned down |
# - Added colourised output when tput is not capable (i.e. FreeBSD) |
# - Added prefixes when colour is not available for console output |
# - Don't print lsdvd error channel is DVD mode |
# - Suppress mv errors (e.g. over VFS being unable to preserve) |
# * Minimum ImageMagick version set to 6.3.5-7 |
# * Better detection of requirements (e.g. disallow decoders without png |
# support) |
# * Allow overriding height, number of captures, interval, columns, and |
# padding |
# * UNDOCUMENTED/DEBUG: |
# - Allow stopping the main loop before cleaning up (--undocumented hang) |
# - Identification-only mode. Might be promoted to an actual feature |
# (--undocumented idonly) |
# - Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
# set_mplayer) |
# - Allow disabling either mplayer of ffmpeg (as if they weren't |
# installed (--undocumented disable_ffmpeg and disable_mplayer) |
# - Added -Wc to disable console colour, repeat to disable prefixes |
# * INTERNAL: |
# - assert() |
# - Cleanup: correctness checks converted to asserts, removal of old dead |
# code |
# - Typos |
# |
# }}} # CHANGELOG |
set -e |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# TODO / FIXME: |
# * (1.12) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --shoehorn -> --undocumented shoehorn 1.11 1.12 |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# MIN_LENGTH_FOR_END_OFFSET -> none 1.11 1.12 or 1.13 |
# * No warning shown in 1.11. 1.12 will switch back default end offset either to 0 or a |
# percentage *and* use it always (disable with -E0) |
# end offset -> none ? ?+1 |
# --mincho -> --nonlatin 1.11 1.12 |
# * Better check for coherence of overrides |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height= # *WILL CHANGE NAME* |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
'th_height' |
'interval' |
'numcaps' |
'HPAD' |
'cols' |
# Note GETOPT doesn't make sense to be overridden from the command-line |
'GETOPT' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
end_offset=$DEFAULT_END_OFFSET |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=${ALLOWED_OVERRIDES[*]} |
compregex=${compregex// /|} # = s ' ' => '|' |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really work anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(egrep -o '^[[:space:]]*[a-zA-Z0-9_]*=.' <<<"$o" | cut -d'=' -f1 | egrep -o '[^ ]*') |
local varval=$(egrep -o '^[^=]*=.*' <<<"$o" | cut -d'=' -f2-) |
if [ "$varname" = "DEFAULT_INTERVAL" ]; then |
warn '$DEFAULT_INTERVAL is deprecated, please use $interval instead' |
varname=interval |
elif [ "$varname" = "DEFAULT_NUMCAPS" ]; then |
warn '$DEFAULT_NUMCAPS is deprecated, please use $numcaps instead' |
varname=numcaps |
elif [ "$varname" = "DEFAULT_COLS" ]; then |
warn '$DEFAULT_COLS is deprecated, please use $cols instead' |
varname=cols |
fi |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
eval "USR_$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input is a valid percentage (xx% or xx.yy%) |
# is_percentage($1 = input) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the all code (default bc -l) |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl($1 = string) |
stonl() { |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
} |
# Converts newlines to spaces portably |
# nltos($1 = string) |
nltos() { |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkex "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkex "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkex "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully sson this won't be needed |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$DVD_FILE" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[*]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 >/dev/null && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$(awkex "$end - $st") |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
# Note mplayer CAN'T set the output filename |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$DVD_FILE" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkex "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
#mp4v|MP4V) vcodec="MPEG-4" ;; |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$stderr" | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>"$stderr" | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
# (AFAIK) Can't use ffmpeg in DVD Mode |
#[ $DVD_MODE -eq 0 ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locale mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed 's/:/h/' | sed 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkex "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
[ -z "$ffl" ] && [ $DVD_MODE -eq 1 ] && ffl="(unavailable in DVD mode)" |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:" |
inf "$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ] ; then |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Override-able options check, in case they were set from overrides instead |
#+of equivalent command-line options. Will check the actual selected values, |
#+i.e. fail silently if the overrides aren't effective |
[ "$USR_th_height" ] && { check_height "$th_height" || exit $? ; } |
[ "$USR_numcaps" ] && { check_numcaps "$numcaps" || exit $? ; } |
[ "$USR_interval" ] && { check_interval "$interval" || exit $? ; } |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
# Any default font in use? If all of them are overridden, return |
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return |
# Try to locate DejaVu Sans |
[ ! -d /usr/share/fonts ] && return |
local dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
if [ -z "$dvs" ]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[ -z "$USR_font_heading" ] && font_heading="$dvs" |
[ -z "$USR_font_title" ] && font_title="$dvs" |
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs" |
[ -z "$USR_font_sign" ] && font_sign="$dvs" |
[ $DEBUG -eq 1 ] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitization: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
check_height() { # Acceptable height |
if ! is_number "$1" && ! is_percentage "$1" ; then |
error "Height must be a (positive) number or a percentage. Got '$1'." |
return $EX_USAGE |
fi |
} |
check_numcaps() { # Acceptable numcaps |
if ! is_number "$1" ; then |
error "Number of captures must be a (positive) a number! Got '$1'." |
return $EX_USAGE |
fi |
if [ $1 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$1'." |
return $EX_USAGE |
fi |
} |
check_interval() { # Acceptable interval |
if ! get_interval "$1" >/dev/null ; then |
error "Incorrect interval format. Got '$1'." |
return $EX_USAGE |
fi |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
DVD_DEVICE= |
local dvdn=$(realpathr "$f") # dvdn might be a device or an ISO |
if [ -f "$dvdn" ]; then |
# It's an ISO |
DVD_MOUNTP=$(get_dvd_image_mountpoint) |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
elif [ ! -r "$dvdn" ]; then |
# It's something else we cannot read |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_DEVICE="$dvdn" |
DVD_MOUNTP=$(mount | grep -o "^$DVD_DEVICE *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD" |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$DVD_FILE" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
unset dt |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS)" |
fi |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" ; then |
local pc=${th_height/%%/} # BASH %% == RE %$ |
vidcap_height=$(awkex "int ((${VID[$H]} * ${pc}) / 100 + 0.5)") |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkex "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkex "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$DVD_FILE" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# Need a mountpoint to get the actual *title* size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Title size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.3576 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:V:R:Z:o:P: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:,randomsource:,undocumented:,output:,fullhelp,profile:,"\ |
"jpeg2,nonlatin" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_interval "$2" |
interval=$(get_interval "$2") |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_numcaps "$2" |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
USR_numcaps="$2" |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_end_offset="$end_offset" |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
--shoehorn) |
warn "$1 is deprecated, please use '--undocumented shoehorn=\"$2\"' instead" |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_height "$2" |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
--mincho) |
warn "--mincho is deprecated, please use -Ij or --nonlatin instead" |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
override "$2" "command line" |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkex "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-P|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
esac |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
;; |
*) false ;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/Makefile |
---|
0,0 → 1,44 |
# $Id$ |
prefix:=/usr/local |
DESTDIR:=/ |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/') |
PACKAGER:=$(shell echo $$DEBFULLNAME) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
endif |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
all: |
# Nothing to be done |
dist: vcs.spec PKGBUILD |
install: |
install -D -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
-rmdir -p $(DESTDIR)$(prefix)/bin |
vcs.spec: rpm/vcs.spec.in |
test -f vcs -a "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
PKGBUILD: arch/PKGBUILD.in |
test -f vcs -a "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e 's!@MD5@!'\''$(shell bzip2 -c9 < vcs | md5sum -b - | cut -d' ' -f1)'\''!g' \ |
-e 's!@SHA1@!'\''$(shell bzip2 -c9 < vcs | sha1sum -b - | cut -d' ' -f1)'\''!g' > $@ |
clean: |
distclean: |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,97 |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
%install |
make DESTDIR=%buildroot prefix=/usr install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
%changelog |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/debian/changelog |
---|
0,0 → 1,56 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 12 Mar 2010 01:36:10 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/CHANGELOG |
---|
0,0 → 1,320 |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,28 |
# Maintainer: Toni Corvera <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
source=($url/files/$pkgname-$pkgver.bz2) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch don't agree on this (???) sha256sums=() |
build() { |
# cd $srcdir/$pkgname-$pkgver |
# bzip2 -dc $pkgname-$pkgver.bz2 > $pkgname-$pkgver |
install -D -m755 $pkgname-$pkgver ${pkgdir}/usr/bin/$pkgname || return 1 |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/Makefile |
---|
0,0 → 1,59 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre2 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/vcs |
---|
0,0 → 1,4087 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
declare -r VERSION="1.11.2" |
declare -r RELEASE=1 |
# {{{ # CHANGELOG |
# Last release changes: |
# (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.11.2: |
# * BUGFIXES: |
# - Remove extra, empty, temporary dir |
# - Use standard awk syntax for exponentiation (pyth_th) |
# - Woraround for systems that don't register fonts with ImageMagick |
# 1.11.1: |
# * Added FLV1 codec |
# * BUGFIXES: |
# - Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
# overrides, warn about their new names (interval, numcaps and cols) |
# - Fix ImageMagick version detection |
# 1.11: |
# * FEATURES |
# - Allow setting output filename. With extension will set output format, |
# without will inherit it. |
# - Allow percentages in height. |
# - Require mplayer OR ffmpeg instead of both. Having both is still |
# recommended for better results. |
# - Safe mode, for files whose length doesn't get reported correctly. |
# Completely automated. |
# Number of tries can be increased with -Ws. Repeat to increase further. |
# Use -WS to do try as many times as possible. |
# Accuracy (stepping) can be increased with -Wp. Repeat to increase |
# accuracy. Decrease with -WP. |
# Can be deliberately disabled with -Wb to force processing of broken |
# files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
# - Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
# * BUGFIXES: |
# - Don't pass ms to mplayer. It ignores them anyway and in some rare |
# cases breaks the last capture (possibly due to the 5-frames hack) |
# - Honor detected aspect ratio if found |
# - Try to detect files that might fail on the last capture and trigger |
# safe mode |
# - Timestamps font was being ignored. As a side effect this produced |
# italiced timestamps in some systems |
# - Fixed obscure bug with safe_rename_pattern overrides |
# * COMPAT: Support for bash 2.05b. This will (probably) be the last version |
# capable of running under bash 2. |
# * DVD mode revamp |
# - Print title file size instead of disc size when possible |
# - Aspect ratio detection, if available |
# - Use of FFmpeg if available to get better information |
# - Mostly x-platform, only ISOs identification is a bit better in Linux |
# * Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
# (MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
# Video codec renamings: |
# - TechSmith codec name shortened to TechSmith SCC |
# - Raw RGB renamed to Raw video |
# * Help cleanup. The default help output is much shorter, the full text |
# can be displayed with --fullhelp. Also print the decoder choice near |
# the appropriate option (-M/-F) |
# * Added --anonymous to help (never was in it) |
# * Drop requirement on seq/jot and bc, replaced by inline awk |
# ... New requirement: Perl (only for DVDs). |
# * Adopt new/fixed numbering scheme |
# <http://p.outlyer.net/dox/vcs:devel:renumbering> |
# * Check ImageMagick version (must decide which is the real minimum |
# required) |
# * Non-latin fonts revamp: |
# - -I no longer works alone (use -Ij or -Ik instead) |
# - -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
# - -I accepts a font name or font filename like |
# -Ij=Kochi-Mincho-Regular or |
# -Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# * Deprecated options: |
# --shoehorn: Will be removed unless someone really needs it. |
# --mincho: Replaced by --nonlatin |
# * COSMETIC: |
# - Default font switched to DejaVu Sans. |
# Font sizes reduced to accomodate the new default. |
# Should fall back to a sane default if it's not available |
# - Much tighter padding |
# - Smaller timestamps font by default |
# - Print friendlier timestamp when a capture fails |
# - Print program signature to console without colour |
# - Use main font by default in timestamps |
# - Heading background colour toned down |
# - Added colourised output when tput is not capable (i.e. FreeBSD) |
# - Added prefixes when colour is not available for console output |
# - Don't print lsdvd error channel is DVD mode |
# - Suppress mv errors (e.g. over VFS being unable to preserve) |
# * Minimum ImageMagick version set to 6.3.5-7 |
# * Better detection of requirements (e.g. disallow decoders without png |
# support) |
# * Allow overriding height, number of captures, interval, columns, and |
# padding |
# * UNDOCUMENTED/DEBUG: |
# - Allow stopping the main loop before cleaning up (--undocumented hang) |
# - Identification-only mode. Might be promoted to an actual feature |
# (--undocumented idonly) |
# - Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
# set_mplayer) |
# - Allow disabling either mplayer of ffmpeg (as if they weren't |
# installed (--undocumented disable_ffmpeg and disable_mplayer) |
# - Added -Wc to disable console colour, repeat to disable prefixes |
# * INTERNAL: |
# - assert() |
# - Cleanup: correctness checks converted to asserts, removal of old dead |
# code |
# - Typos |
# |
# }}} # CHANGELOG |
set -e |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# TODO / FIXME: |
# * (1.12) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --shoehorn -> --undocumented shoehorn 1.11 1.12 |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# MIN_LENGTH_FOR_END_OFFSET -> none 1.11 1.12 or 1.13 |
# * No warning shown in 1.11. 1.12 will switch back default end offset either to 0 or a |
# percentage *and* use it always (disable with -E0) |
# end offset -> none ? ?+1 |
# --mincho -> --nonlatin 1.11 1.12 |
# * Better check for coherence of overrides |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height= # *WILL CHANGE NAME* |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
'th_height' |
'interval' |
'numcaps' |
'HPAD' |
'cols' |
# Note GETOPT doesn't make sense to be overridden from the command-line |
'GETOPT' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
end_offset=$DEFAULT_END_OFFSET |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=${ALLOWED_OVERRIDES[*]} |
compregex=${compregex// /|} # = s ' ' => '|' |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really work anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(egrep -o '^[[:space:]]*[a-zA-Z0-9_]*=.' <<<"$o" | cut -d'=' -f1 | egrep -o '[^ ]*') |
local varval=$(egrep -o '^[^=]*=.*' <<<"$o" | cut -d'=' -f2-) |
if [ "$varname" = "DEFAULT_INTERVAL" ]; then |
warn '$DEFAULT_INTERVAL is deprecated, please use $interval instead' |
varname=interval |
elif [ "$varname" = "DEFAULT_NUMCAPS" ]; then |
warn '$DEFAULT_NUMCAPS is deprecated, please use $numcaps instead' |
varname=numcaps |
elif [ "$varname" = "DEFAULT_COLS" ]; then |
warn '$DEFAULT_COLS is deprecated, please use $cols instead' |
varname=cols |
fi |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
eval "USR_$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input is a valid percentage (xx% or xx.yy%) |
# is_percentage($1 = input) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the all code (default bc -l) |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl($1 = string) |
stonl() { |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
} |
# Converts newlines to spaces portably |
# nltos($1 = string) |
nltos() { |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkex "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkex "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkex "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
# Hopefully sson this won't be needed |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$DVD_FILE" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[*]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 >/dev/null && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$(awkex "$end - $st") |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
# Note mplayer CAN'T set the output filename |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$DVD_FILE" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkex "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
#mp4v|MP4V) vcodec="MPEG-4" ;; |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>/dev/null | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>/dev/null | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
# (AFAIK) Can't use ffmpeg in DVD Mode |
#[ $DVD_MODE -eq 0 ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locale mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed 's/:/h/' | sed 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkex "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
[ -z "$ffl" ] && [ $DVD_MODE -eq 1 ] && ffl="(unavailable in DVD mode)" |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:" |
inf "$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ] ; then |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Override-able options check, in case they were set from overrides instead |
#+of equivalent command-line options. Will check the actual selected values, |
#+i.e. fail silently if the overrides aren't effective |
[ "$USR_th_height" ] && { check_height "$th_height" || exit $? ; } |
[ "$USR_numcaps" ] && { check_numcaps "$numcaps" || exit $? ; } |
[ "$USR_interval" ] && { check_interval "$interval" || exit $? ; } |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
# Any default font in use? If all of them are overridden, return |
if [ "$USR_font_heading" -a "$USR_font_title" -a "$USR_font_tstamps" -a "$USR_font_sign" ]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[ -z "$USR_font_heading" ] && [ "$font_heading" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_title" ] && [ "$font_title" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_tstamps" ] && [ "$font_tstamps" != 'DejaVu-Sans-Book' ] && return |
[ -z "$USR_font_sign" ] && [ "$font_sign" != 'DejaVu-Sans-Book' ] && return |
# Try to locate DejaVu Sans |
[ ! -d /usr/share/fonts ] && return |
local dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
if [ -z "$dvs" ]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[ -z "$USR_font_heading" ] && font_heading="$dvs" |
[ -z "$USR_font_title" ] && font_title="$dvs" |
[ -z "$USR_font_tstamps" ] && font_tstamps="$dvs" |
[ -z "$USR_font_sign" ] && font_sign="$dvs" |
[ $DEBUG -eq 1 ] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitization: |
font_heading: $font_heading |
font_title : $font_title |
font_tstamps: $font_tstamps |
font_sign : $font_sign |
EOFF |
} |
check_height() { # Acceptable height |
if ! is_number "$1" && ! is_percentage "$1" ; then |
error "Height must be a (positive) number or a percentage. Got '$1'." |
return $EX_USAGE |
fi |
} |
check_numcaps() { # Acceptable numcaps |
if ! is_number "$1" ; then |
error "Number of captures must be a (positive) a number! Got '$1'." |
return $EX_USAGE |
fi |
if [ $1 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$1'." |
return $EX_USAGE |
fi |
} |
check_interval() { # Acceptable interval |
if ! get_interval "$1" >/dev/null ; then |
error "Incorrect interval format. Got '$1'." |
return $EX_USAGE |
fi |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
DVD_DEVICE= |
local dvdn=$(realpathr "$f") # dvdn might be a device or an ISO |
if [ -f "$dvdn" ]; then |
# It's an ISO |
DVD_MOUNTP=$(get_dvd_image_mountpoint) |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
elif [ ! -r "$dvdn" ]; then |
# It's something else we cannot read |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_DEVICE="$dvdn" |
DVD_MOUNTP=$(mount | grep -o "^$DVD_DEVICE *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD" |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$DVD_FILE" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
unset dt |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS)" |
fi |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" ; then |
local pc=${th_height/%%/} # BASH %% == RE %$ |
vidcap_height=$(awkex "int ((${VID[$H]} * ${pc}) / 100 + 0.5)") |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkex "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkex "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$DVD_FILE" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# Need a mountpoint to get the actual *title* size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Title size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.3576 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:V:R:Z:o:P: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:,randomsource:,undocumented:,output:,fullhelp,profile:,"\ |
"jpeg2,nonlatin" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_interval "$2" |
interval=$(get_interval "$2") |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_numcaps "$2" |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
USR_numcaps="$2" |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_end_offset="$end_offset" |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
--shoehorn) |
warn "$1 is deprecated, please use '--undocumented shoehorn=\"$2\"' instead" |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_height "$2" |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
--mincho) |
warn "--mincho is deprecated, please use -Ij or --nonlatin instead" |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
override "$2" "command line" |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkex "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-P|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
esac |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
;; |
*) false ;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(realpathr $(type -pf awk)) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/Makefile |
---|
0,0 → 1,44 |
# $Id$ |
prefix:=/usr/local |
DESTDIR:=/ |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/') |
PACKAGER:=$(shell echo $$DEBFULLNAME) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
endif |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell grep ^`id -un` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
endif |
all: |
# Nothing to be done |
dist: vcs.spec PKGBUILD |
install: |
install -D -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
-rmdir -p $(DESTDIR)$(prefix)/bin |
vcs.spec: rpm/vcs.spec.in |
test -f vcs -a "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
PKGBUILD: arch/PKGBUILD.in |
test -f vcs -a "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e 's!@MD5@!'\''$(shell bzip2 -c9 < vcs | md5sum -b - | cut -d' ' -f1)'\''!g' \ |
-e 's!@SHA1@!'\''$(shell bzip2 -c9 < vcs | sha1sum -b - | cut -d' ' -f1)'\''!g' > $@ |
clean: |
distclean: |
-$(RM) vcs.spec PKGBUILD |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/rpm/vcs.spec.in |
---|
0,0 → 1,97 |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
%install |
make DESTDIR=%buildroot prefix=/usr install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
%changelog |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/debian/changelog |
---|
0,0 → 1,56 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 12 Mar 2010 01:36:10 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/CHANGELOG |
---|
0,0 → 1,320 |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/pkg/arch/PKGBUILD.in |
---|
0,0 → 1,28 |
# Maintainer: Toni Corvera <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
source=($url/files/$pkgname-$pkgver.bz2) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch don't agree on this (???) sha256sums=() |
build() { |
# cd $srcdir/$pkgname-$pkgver |
# bzip2 -dc $pkgname-$pkgver.bz2 > $pkgname-$pkgver |
install -D -m755 $pkgname-$pkgver ${pkgdir}/usr/bin/$pkgname || return 1 |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/vcs |
---|
0,0 → 1,0 |
link pkg/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/Makefile |
---|
0,0 → 1,59 |
#!/usr/bin/make -f |
# $Id$ |
srcdir=pkg |
VER=$(shell grep VERSION $(srcdir)/vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
vcs-$(VER).tar.gz: |
cp -rvpP pkg/ vcs-$(VER) |
cd vcs-$(VER) && make dist |
tar zcvf vcs-$(VER).tar.gz --exclude '.svn' --exclude '*.swp' --exclude '*.swo' vcs-$(VER) |
$(RM) -r vcs-$(VER) |
check-no-svn: |
#@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
vcs-$(VER).gz vcs-$(VER).bz2 vcs-$(VER).bash \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: |
$(RM) -ri vcs Makefile *.changes pkg |
deb: |
cd pkg && debuild -us -uc -b && debclean |
$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.2pre1 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.11.1/arch-package/PKGBUILD |
---|
0,0 → 1,28 |
# Maintainer: Toni Corvera <outlyer@gmail.com> |
pkgname=vcs |
pkgver=1.11 |
pkgrel=1.upstream |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=2.05b' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
source=($url/files/$pkgname-$pkgver.bz2) |
md5sums=('c0cc4152278d93836bfc920e9e7a1078') #generate with 'makepkg -g' |
sha1sums=('f08f07ba655355942a36a8ee695d88c86965a471') |
sha256sums=('056233236a3ed06249af652e5d11e843586d025974b131c173b38a06bd8f2bbd') |
build() { |
# cd $srcdir/$pkgname-$pkgver |
# bzip2 -dc $pkgname-$pkgver.bz2 > $pkgname-$pkgver |
install -D -m755 $pkgname-$pkgver ${pkgdir}/usr/bin/$pkgname || return 1 |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
/ATTIC/video-contact-sheet/tags/1.11.1/CHANGELOG |
---|
0,0 → 1,320 |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/debian/changelog |
---|
0,0 → 1,50 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.11.1/debian-package/Makefile |
---|
0,0 → 1,14 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
all: |
clean: |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/vcs |
---|
0,0 → 1,4053 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
declare -r VERSION="1.11.1" |
declare -r RELEASE=1 |
# {{{ # CHANGELOG |
# Last release changes: |
# (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.11.1: |
# * Added FLV1 codec |
# * BUGFIXES: |
# - Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
# overrides, warn about their new names (interval, numcaps and cols) |
# - Fix ImageMagick version detection |
# 1.11: |
# * FEATURES |
# - Allow setting output filename. With extension will set output format, |
# without will inherit it. |
# - Allow percentages in height. |
# - Require mplayer OR ffmpeg instead of both. Having both is still |
# recommended for better results. |
# - Safe mode, for files whose length doesn't get reported correctly. |
# Completely automated. |
# Number of tries can be increased with -Ws. Repeat to increase further. |
# Use -WS to do try as many times as possible. |
# Accuracy (stepping) can be increased with -Wp. Repeat to increase |
# accuracy. Decrease with -WP. |
# Can be deliberately disabled with -Wb to force processing of broken |
# files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
# - Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
# * BUGFIXES: |
# - Don't pass ms to mplayer. It ignores them anyway and in some rare |
# cases breaks the last capture (possibly due to the 5-frames hack) |
# - Honor detected aspect ratio if found |
# - Try to detect files that might fail on the last capture and trigger |
# safe mode |
# - Timestamps font was being ignored. As a side effect this produced |
# italiced timestamps in some systems |
# - Fixed obscure bug with safe_rename_pattern overrides |
# * COMPAT: Support for bash 2.05b. This will (probably) be the last version |
# capable of running under bash 2. |
# * DVD mode revamp |
# - Print title file size instead of disc size when possible |
# - Aspect ratio detection, if available |
# - Use of FFmpeg if available to get better information |
# - Mostly x-platform, only ISOs identification is a bit better in Linux |
# * Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
# (MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
# Video codec renamings: |
# - TechSmith codec name shortened to TechSmith SCC |
# - Raw RGB renamed to Raw video |
# * Help cleanup. The default help output is much shorter, the full text |
# can be displayed with --fullhelp. Also print the decoder choice near |
# the appropriate option (-M/-F) |
# * Added --anonymous to help (never was in it) |
# * Drop requirement on seq/jot and bc, replaced by inline awk |
# ... New requirement: Perl (only for DVDs). |
# * Adopt new/fixed numbering scheme |
# <http://p.outlyer.net/dox/vcs:devel:renumbering> |
# * Check ImageMagick version (must decide which is the real minimum |
# required) |
# * Non-latin fonts revamp: |
# - -I no longer works alone (use -Ij or -Ik instead) |
# - -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
# - -I accepts a font name or font filename like |
# -Ij=Kochi-Mincho-Regular or |
# -Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# * Deprecated options: |
# --shoehorn: Will be removed unless someone really needs it. |
# --mincho: Replaced by --nonlatin |
# * COSMETIC: |
# - Default font switched to DejaVu Sans. |
# Font sizes reduced to accomodate the new default. |
# Should fall back to a sane default if it's not available |
# - Much tighter padding |
# - Smaller timestamps font by default |
# - Print friendlier timestamp when a capture fails |
# - Print program signature to console without colour |
# - Use main font by default in timestamps |
# - Heading background colour toned down |
# - Added colourised output when tput is not capable (i.e. FreeBSD) |
# - Added prefixes when colour is not available for console output |
# - Don't print lsdvd error channel is DVD mode |
# - Suppress mv errors (e.g. over VFS being unable to preserve) |
# * Minimum ImageMagick version set to 6.3.5-7 |
# * Better detection of requirements (e.g. disallow decoders without png |
# support) |
# * Allow overriding height, number of captures, interval, columns, and |
# padding |
# * UNDOCUMENTED/DEBUG: |
# - Allow stopping the main loop before cleaning up (--undocumented hang) |
# - Identification-only mode. Might be promoted to an actual feature |
# (--undocumented idonly) |
# - Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
# set_mplayer) |
# - Allow disabling either mplayer of ffmpeg (as if they weren't |
# installed (--undocumented disable_ffmpeg and disable_mplayer) |
# - Added -Wc to disable console colour, repeat to disable prefixes |
# * INTERNAL: |
# - assert() |
# - Cleanup: correctness checks converted to asserts, removal of old dead |
# code |
# - Typos |
# |
# }}} # CHANGELOG |
set -e |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# TODO / FIXME: |
# * (1.12) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --shoehorn -> --undocumented shoehorn 1.11 1.12 |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# MIN_LENGTH_FOR_END_OFFSET -> none 1.11 1.12 or 1.13 |
# * No warning shown in 1.11. 1.12 will switch back default end offset either to 0 or a |
# percentage *and* use it always (disable with -E0) |
# end offset -> none ? ?+1 |
# --mincho -> --nonlatin 1.11 1.12 |
# * Better check for coherence of overrides |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Default values, use interval, numcaps and cols to override |
declare -ri DEFAULT_INTERVAL=300 |
declare -ri DEFAULT_NUMCAPS=16 |
declare -ri DEFAULT_COLS=2 |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height= # *WILL CHANGE NAME* |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
'th_height' |
'interval' |
'numcaps' |
'HPAD' |
'cols' |
# Note GETOPT doesn't make sense to be overridden from the command-line |
'GETOPT' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
end_offset=$DEFAULT_END_OFFSET |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=${ALLOWED_OVERRIDES[*]} |
compregex=${compregex// /|} # = s ' ' => '|' |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really work anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(egrep -o '^[[:space:]]*[a-zA-Z0-9_]*=.' <<<"$o" | cut -d'=' -f1 | egrep -o '[^ ]*') |
local varval=$(egrep -o '^[^=]*=.*' <<<"$o" | cut -d'=' -f2-) |
if [ "$varname" = "DEFAULT_INTERVAL" ]; then |
warn '$DEFAULT_INTERVAL is deprecated, please use $interval instead' |
varname=interval |
elif [ "$varname" = "DEFAULT_NUMCAPS" ]; then |
warn '$DEFAULT_NUMCAPS is deprecated, please use $numcaps instead' |
varname=numcaps |
elif [ "$varname" = "DEFAULT_COLS" ]; then |
warn '$DEFAULT_COLS is deprecated, please use $cols instead' |
varname=cols |
fi |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
eval "USR_$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input is a valid percentage (xx% or xx.yy%) |
# is_percentage($1 = input) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the all code (default bc -l) |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl($1 = string) |
stonl() { |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
} |
# Converts newlines to spaces portably |
# nltos($1 = string) |
nltos() { |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkex "sqrt($1 ** 2 + $2 ** 2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkex "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkex "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$DVD_FILE" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick[[:space:]]*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 >/dev/null && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$(awkex "$end - $st") |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
# Note mplayer CAN'T set the output filename |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$DVD_FILE" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# create_temp_dir should have been called |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
create_temp_dir |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkex "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
#mp4v|MP4V) vcodec="MPEG-4" ;; |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>/dev/null | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>/dev/null | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
# (AFAIK) Can't use ffmpeg in DVD Mode |
#[ $DVD_MODE -eq 0 ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locale mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed 's/:/h/' | sed 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkex "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
create_temp_dir |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
[ -z "$ffl" ] && [ $DVD_MODE -eq 1 ] && ffl="(unavailable in DVD mode)" |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:" |
inf "$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ] ; then |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Override-able options check, in case they were set from overrides instead |
#+of equivalent command-line options. Will check the actual selected values, |
#+i.e. fail silently if the overrides aren't effective |
[ "$USR_th_height" ] && { check_height "$th_height" || exit $? ; } |
[ "$USR_numcaps" ] && { check_numcaps "$numcaps" || exit $? ; } |
[ "$USR_interval" ] && { check_interval "$interval" || exit $? ; } |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
} |
check_height() { # Acceptable height |
if ! is_number "$1" && ! is_percentage "$1" ; then |
error "Height must be a (positive) number or a percentage. Got '$1'." |
return $EX_USAGE |
fi |
} |
check_numcaps() { # Acceptable numcaps |
if ! is_number "$1" ; then |
error "Number of captures must be a (positive) a number! Got '$1'." |
return $EX_USAGE |
fi |
if [ $1 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$1'." |
return $EX_USAGE |
fi |
} |
check_interval() { # Acceptable interval |
if ! get_interval "$1" >/dev/null ; then |
error "Incorrect interval format. Got '$1'." |
return $EX_USAGE |
fi |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
DVD_DEVICE= |
local dvdn=$(realpathr "$f") # dvdn might be a device or an ISO |
if [ -f "$dvdn" ]; then |
# It's an ISO |
DVD_MOUNTP=$(get_dvd_image_mountpoint) |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
elif [ ! -r "$dvdn" ]; then |
# It's something else we cannot read |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_DEVICE="$dvdn" |
DVD_MOUNTP=$(mount | grep -o "^$DVD_DEVICE *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD" |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$DVD_FILE" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
unset dt |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS)" |
fi |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" ; then |
local pc=${th_height/%%/} # BASH %% == RE %$ |
vidcap_height=$(awkex "int ((${VID[$H]} * ${pc}) / 100 + 0.5)") |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkex "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkex "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$DVD_FILE" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# Need a mountpoint to get the actual *title* size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Title size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.3576 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:V:R:Z:o:P: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:,randomsource:,undocumented:,output:,fullhelp,profile:,"\ |
"jpeg2,nonlatin" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_interval "$2" |
interval=$(get_interval "$2") |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_numcaps "$2" |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
USR_numcaps="$2" |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_end_offset="$end_offset" |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
--shoehorn) |
warn "$1 is deprecated, please use '--undocumented shoehorn=\"$2\"' instead" |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_height "$2" |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
--mincho) |
warn "--mincho is deprecated, please use -Ij or --nonlatin instead" |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
override "$2" "command line" |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkex "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-P|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
esac |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
;; |
*) false ;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(type -pf awk) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/rpm-package/vcs.spec.in |
---|
0,0 → 1,97 |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
%install |
make DESTDIR=%buildroot install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
%changelog |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/rpm-package/Makefile |
---|
0,0 → 1,33 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/') |
#PACKAGER=$(shell grep ^`id -nu` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
all: |
@echo 'There'\''s nothing to be built' |
@echo 'Available make commands:' |
@echo '* Build RPM (and generate spec)' |
@echo ' $$ make rpm' |
@echo '* Generate spec file' |
@echo ' $$ make spec' |
@echo '* Install' |
@echo ' $$ make install' |
clean: |
$(RM) vcs.spec |
spec: vcs.spec.in |
test "$(VERSION)" |
@test "$(PACKAGER)" || echo "No PACKAGER set!" |
@test "$(PACKAGER)" |
cat vcs.spec.in | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > vcs.spec |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean spec |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/Makefile |
---|
0,0 → 1,63 |
#!/usr/bin/make -f |
# $Id$ |
VER=$(shell grep VERSION vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
check-no-svn: |
@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
prep: |
cp vcs CHANGELOG debian-package/ |
cp vcs rpm-package/ |
dist: check-rel check-no-svn prep gz bz2 plaintext changelog deb rpm cleanup |
gz: |
cp vcs vcs-$(VER) |
chmod -x vcs-$(VER) |
gzip -9 vcs-$(VER) |
bz2: |
cp vcs vcs-$(VER) |
chmod -x vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
plaintext: |
cp vcs vcs-$(VER) |
chmod -x vcs-$(VER) |
changelog: |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
cleanup: |
$(RM) vcs Makefile *.changes |
$(RM) -r debian-package |
$(RM) -r rpm-package |
deb: |
cd debian-package/ && dpkg-buildpackage -rfakeroot -us -uc -b |
rpm: vcs.spec |
mkdir rpm-package/vcs-$(VER)/ |
cp vcs CHANGELOG rpm-package/Makefile rpm-package/vcs-$(VER)/ |
mv vcs.spec rpm-package/vcs-$(VER)/ |
cd rpm-package && tar zcvf vcs-$(VER).tar.gz vcs-$(VER) |
$(RM) vcs.spec |
$(RM) -r rpm-package/vcs-$(VER) |
cd rpm-package && fakeroot rpmbuild -tb vcs-$(VER).tar.gz |
-ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . |
$(RM) rpm-package/vcs-$(VER).tar.gz |
vcs.spec: rpm-package/vcs.spec.in |
cd rpm-package && $(MAKE) -f Makefile spec PACKAGER="$(PACKAGER)" |
mv rpm-package/vcs.spec . |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11.1 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.11/vcs |
---|
0,0 → 1,4030 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
declare -r VERSION="1.11" |
declare -r RELEASE=1 |
# {{{ # CHANGELOG |
# Last release changes: |
# (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.11: |
# * FEAT: Allow setting output filename. With extension will set output |
# format, without will inherit it. |
# * FEAT: Require mplayer OR ffmpeg instead of both. Having both is still |
# recommended for better results. |
# * FEAT: Safe mode, for files whose length doesn't get reported correctly. |
# Completely automated. |
# Number of tries can be increased with -Ws. Repeat to increase |
# further. Use -WS to do try as many times as possible. |
# Accuracy (stepping) can be increased with -Wp. Repeat to increase |
# accuracy. Decrease with -WP. |
# Can be deliberately disabled with -Wb to force processing of |
# broken files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
# * FEAT: Allow percentages in height. |
# * BUGFIX: Don't pass ms to mplayer. It ignores them anyway and in some |
# rare cases breaks the last capture (possibly due to the 5-frames |
# hack) |
# * BUGFIX: Honor detected aspect ratio if found |
# * BUGFIX: Try to detect files that might fail on the last capture and |
# trigger safe mode |
# * BUGFIX: Timestamps font was being ignored. As a side effect this produced |
# italiced timestamps in some systems. |
# * BUGFIX: Fixed obscure bug with safe_rename_pattern overrides |
# * COMPAT: Support for bash 2.05b. This will (probably) be the last version |
# capable of running under bash 2. |
# * DVD mode revamp |
# - Print title file size instead of disc size when possible |
# - Aspect ratio detection, if available |
# - Use of FFmpeg if available to get better information |
# - Mostly x-platform, only ISOs identification is a bit better in Linux |
# * Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
# (MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
# Video codec renamings: |
# - TechSmith codec name shortened to TechSmith SCC |
# - Raw RGB renamed to Raw video |
# * Help cleanup. The default help output is much shorter, the full text |
# can be displayed with --fullhelp. Also print the decoder choice near |
# the appropriate option (-M/-F) |
# * Added --anonymous to help (never was in it) |
# * Drop requirement on seq/jot and bc, replaced by inline awk |
# ... New requirement: Perl. It's required by POSIX anyway |
# * Adopt new/fixed numbering scheme |
# <http://p.outlyer.net/dox/vcs:devel:renumbering> |
# * Check ImageMagick version (must decide which is the real minimum |
# required) |
# * Non-latin fonts revamp: |
# - -I no longer works alone (use -Ij or -Ik instead) |
# - -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
# - -I accepts a font name or font filename like |
# -Ij=Kochi-Mincho-Regular or |
# -Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# * Deprecated options: |
# --shoehorn: Will be removed unless someone really needs it. |
# --mincho: Replaced by --nonlatin |
# * COSMETIC: |
# - Default font switched to DejaVu Sans. |
# Font sizes reduced to accomodate the new default. |
# Should fall back to a sane default if it's not available |
# - Much tighter padding |
# - Smaller timestamps font by default |
# - Print friendlier timestamp when a capture fails |
# - Print program signature to console without colour |
# - Use main font by default in timestamps |
# - Heading background colour toned down |
# - Added colourised output when tput is not capable (i.e. FreeBSD) |
# - Added prefixes when colour is not available for console output |
# - Don't print lsdvd error channel is DVD mode |
# - Suppress mv errors (e.g. over VFS being unable to preserve) |
# * UNDOCUMENTED/DEBUG: |
# - Allow stopping the main loop before cleaning up (--undocumented hang) |
# - Identification-only mode. Might be promoted to an actual feature |
# (--undocumented idonly) |
# - Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
# set_mplayer) |
# - Allow disabling either mplayer of ffmpeg (as if they weren't |
# installed (--undocumented disable_ffmpeg and disable_mplayer) |
# - Added -Wc to disable console colour, repeat to disable prefixes |
# * INTERNAL: assert() |
# * INTERNAL: Cleanup: correctness checks converted to asserts, removal |
# of old dead code, fixed typos in comments |
# * Fixed various typos and cut out lines in CHANGELOG |
# * Minimum ImageMagick version set to 6.3.5-7 |
# * Better detection of requirements (e.g. disallow decoders without png |
# support) |
# * Allow overriding height, number of captures, interval, columns, and |
# padding |
# * Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
# }}} # CHANGELOG |
set -e |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 2.05b |
if [ "${BASH_VERSINFO[0]}" -le 2 ]; then # Only bash <=2 needs extra testing |
# I guess we can't expect any new bash2 release |
if [ "${BASH_VERSINFO[0]}" -lt 2 ] || |
( [ "${BASH_VERSINFO[0]}" -eq 2 ] && [ "${BASH_VERSINFO[1]}" != '05b' ] ); then |
echo "Bash 2.05b or higher is required" >&2 |
exit 1 |
fi |
fi |
} |
# {{{ # TO-DO |
# TODO / FIXME: |
# * (1.12) Start replacing 'grep' with bash's '[[ =~ ]]'. Will break bash 2 compatibility for good |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. => |
#+ SUS v2: egrep is deprecated, grep -E replaces it |
# * Change default DVD_TITLE to 0 |
# * Deprecations: |
# OPTION/VAR -> ALTERNATIVE DEPRECATED FROM VERSION REMOVAL ETA |
# --shoehorn -> --undocumented shoehorn 1.11 1.12 |
# --undocumented shoehorn -> NONE 1.12 1.13 or 1.14 |
# --funky -> --profile ? ?+1 |
# MIN_LENGTH_FOR_END_OFFSET -> none 1.11 1.12 or 1.13 |
# * No warning shown in 1.11. 1.12 will switch back default end offset either to 0 or a |
# percentage *and* use it always (disable with -E0) |
# end offset -> none ? ?+1 |
# --mincho -> --nonlatin 1.11 1.12 |
# * Better check for coherence of overrides |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line |
# }}} # TO-DO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir config, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading='#afcd7a' # Background for meta info (size, codec...) |
declare bg_sign=SlateGray #'#a2a9af' # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background for the captures |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practica it prints an error |
declare font_tstamps=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare font_heading=DejaVu-Sans-Book # Used for the meta info heading |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used for the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=14 # Used for the timestamps |
declare -i pts_meta=14 # Used for the meta info heading |
declare -i pts_sign=10 # Used for the signature |
declare -i pts_title=33 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# Set to 1 to disable colours in console output |
declare -i plain_messages=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Introduced in 1.0.7b, revamped in 1.11: |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users actually needing this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare FONT_MINCHO= # Filename or font name as known to ImageMagick (identify -list font) |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare th_height= # *WILL CHANGE NAME* |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
# This is the horizontal padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i HPAD=2 # *WILL CHANGE NAME* |
declare -i cols=$DEFAULT_COLS # Number of output columns |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER= |
declare FFMPEG= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
'th_height' |
'interval' |
'numcaps' |
'HPAD' |
'cols' |
# Note GETOPT doesn't make sense to be overridden from the command-line |
'GETOPT' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
end_offset=$DEFAULT_END_OFFSET |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=${ALLOWED_OVERRIDES[*]} |
compregex=${compregex// /|} # = s ' ' => '|' |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really work anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(egrep -o '^[[:space:]]*[a-zA-Z0-9_]*=.' <<<"$o" | cut -d'=' -f1 | egrep -o '[^ ]*') |
local varval=$(egrep -o '^[^=]*=.*' <<<"$o" | cut -d'=' -f2-) |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
eval "USR_$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input is a valid percentage (xx% or xx.yy%) |
# is_percentage($1 = input) |
is_percentage() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$'<<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" && return $EX_SOFTWARE |
esac |
# Empty operands |
if [ -z "$1" ] || [ -z "$3" ]; then |
assert $LINENO "[ \"'$1' '$2' '$3'\" ] && false" |
else |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
fi |
} |
# Keep a number of decimals *rounded* |
keepdecimals() { |
local N="$1" D="$2" |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
grep -q '\.' <<<"$1" || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkex($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the all code (default bc -l) |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl($1 = string) |
stonl() { |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
} |
# Converts newlines to spaces portably |
# nltos($1 = string) |
nltos() { |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -o '\.[^.]*$' <<<"$1" | cut -d. -f2 |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkex "sqrt($1 ** 2 + $2 ** 2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkex "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") t r n |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# Two consecutive dots are no longer accepted |
if egrep -q '\.\.' <<<"$s" ; then |
return $EX_USAGE |
fi |
# Newer parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# Split into lines of time + unit: |
t= |
for item in $(echo "$s" | grep -o '[0-9]*[hms]') ;do |
n="\"$(echo $item | grep -o '[0-9]*')\"" # Number, quoted |
t=$t$n$(echo $item | grep -o '[hms]') # + Number + Unit |
done |
# Split all ms or s.ms |
for item in $(echo "$s" | grep -o '[0-9]*\.[0-9]*') ;do |
t="${t}\"$item\" + " |
done |
# Seconds without unit. They must be preceded by h, m or s at this point |
local secs=$(echo $s | egrep -o '.?[0-9]*$') |
# When preceded by '.', they're ms |
[ "$secs" ] && grep -q '\.'<<<"$secs" && secs= |
# Quote and addition. Note BSD grep/egrep wants the anchor ($) or won't match |
[ "$secs" ] && secs=" \"$(egrep -o '[0-9]*$'<<<"$secs")\" + " |
t=${t//h/ * 3600 + } |
t=${t//m/ * 60 + } |
t=${t//s/ + } |
t="$t$secs" |
t=${t/% + /} # Strip empty addition |
r=$(awkex "$t" 2>/dev/null) |
# Negative and empty intervals |
assert $LINENO "[ '$r' ] && [ '$t' ]" |
assert $LINENO "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert $LINENO "is_float '$1'" |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=($DEC_MPLAYER==$decoder); |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Clean $safe_rename_pattern, which is override-able |
# Since safe_rename() is called from $() it won't be able to affect global variables directly |
sanitise_rename_pattern() { |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
# Hashmarks will break the regex used in safe_rename() |
if grep -q '#' <<<"$safe_rename_pattern" ; then |
warn "Illegal character \"#\" found in safe renaming pattern, resetting it" |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $FUNCNAME $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$DVD_FILE" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER=$(type -pf mplayer) || true |
FFMPEG=$(type -pf ffmpeg) || true |
# Test we can actually use FFmpeg |
[ "$FFMPEG" ] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
FFMPEG='' |
nopng=1 |
fi |
} |
# Same for Mplayer |
[ "$MPLAYER" ] && { |
if ! "$MPLAYER" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
MPLAYER='' |
nopng=1 |
fi |
} |
[ "$MPLAYER" ] || [ "$FFMPEG" ] || { |
local pngwarn= |
[ $nopng -eq 1 ] && pngwarn=', with PNG output support,' |
error "mplayer and/or ffmpeg$pngwarn are required!" |
let 'retval++,1' |
} |
if [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
decoder=$DEC_MPLAYER |
elif [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
decoder=$DEC_FFMPEG |
fi |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++,1' |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(identify -version | head -n1 | grep -o 'ImageMagick\s*[^ ]*' |\ |
cut -f 2 -d' ') |
if [ "$ver" ]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [ "$serial" -lt 630507 ]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
let 'retval++,1' |
fi |
else |
error "Failed to check ImageMagick version." |
let 'retval++,1' |
fi |
[ $retval -eq 0 ] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_err" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_warn" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n "$prefix_inf" |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
prefix_err='[E] ' |
prefix_inf='[i] ' |
prefix_warn='[w] ' |
suffix_fback= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && tput setaf 0 >/dev/null && tput sgr0 ; then |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
HAS_COLORS="yes" |
fi |
fi |
if [ -z "$HAS_COLORS" ]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
prefix_err=$(echo $'\033[1m\033[31m') |
prefix_warn=$(echo $'\033[1m\033[33m') |
prefix_inf=$(echo $'\033[1m\033[32m') |
suffix_fback=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [ -z "$HAS_COLORS" ]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[ "$inc" ] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# |
# assertion operator |
# assert($1 = line, $* = code) |
# TODO: Limit usage to values that will expand correctly always (i.e. not with quotes) |
assert() { |
[ $RELEASE -eq 1 ] && return |
LINE=$1 |
shift |
eval "$@" || { |
error "Internal error at line $LINE: $@" |
exit $EX_SOFTWARE |
} |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
[ "$VCSTEMPDIR" ] && return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. FIXME: Has AWK or bash something similar? This is the only place requiring perl! |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | grep -o 'Font:.*' | sed -n "${lineno}{p;q;}" | cut -d' ' -f2 |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$(awkex "$end - $st") |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
while fptest $stamp -le "$bound"; do |
assert $LINENO fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) # Don't quote or extra empty stamp! |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame with ffmpeg |
# capture_ffmpeg($1 = inputfile, $2 = outputfile, $3 = timestamp[, $4 = extra opts]) |
capture_ffmpeg() { |
local f=$1 |
local o=$2 |
local ts=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 $shoehorned "$o" >"$stdout" 2>"$stderr" |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame with mplayer |
# capture_mplayer($1 = inputfile, $2 = UNUSED, $3 = timestamp[, $4 = extra opts]) |
capture_mplayer() { |
# Note mplayer CAN'T set the output filename |
local f="$1" |
local o=00000005.png |
local ts=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $shoehorned -dvd-device "$DVD_FILE" \ |
$4 "dvd://$DVD_TITLE" |
else |
"$MPLAYER" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[ -f "$o" ] && [ "0" != "$(du "$o" | cut -f1)" ] |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
capture_mplayer "$f" 'IGNOREME' "$stamp" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# FIXME: ffmpeg can put the temporary file anywhere |
capture_ffmpeg "$f" "$VIDCAPFILE" "$stamp" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || true |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
[ $decoder -eq $DEC_MPLAYER ] && stamp=${stamp/%.*} |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [ $pts -le 7 ]; then |
pts=7 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -stroke none -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -font '$font_tstamps' -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in="$in '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=$HPAD |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local tempfile= |
local ret=0 |
# create_temp_dir should have been called |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [ $decoder -eq $DEC_MPLAYER ]; then |
tempfile=00000005.png |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$tempfile" ) |
if ! capture_mplayer "$f" "IGNOREME" "$ts" "-vf scale=96:96"; then |
ret=1 |
fi |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
tempfile=$(new_temp_file '-safelen.png') |
if ! capture_ffmpeg "$f" "$tempfile" "$ts" "-s 96x96"; then |
ret=1 |
fi |
else |
assert $LINENO false |
ret=1 |
fi |
rm -f "$tempfile" |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported lengths a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $FUNCNAME $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
create_temp_dir |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkex "$len - $rew") 3) |
warn " ... trying $newlen" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
avc1|H264) vcodec="MPEG-4 AVC" ;; # H264 is used in mov/mp4 |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
#mp4v|MP4V) vcodec="MPEG-4" ;; |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
# FIXME List of codec id's I translate but haven't test: |
# svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase while FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr '[a-z]' '[A-Z]') ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
if grep -q '[ -]' <<<"$acid" ; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $VID_MPLAYER with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] || return |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>/dev/null | grep ^ID) |
else |
MPLAYER_CACHE=$("$MPLAYER" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>/dev/null | grep ^ID) |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [ $DVD_MODE -eq 0 ]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[ "${mi[$LEN]}" ] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == "0" ]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[ "${mi[$ASPECT]}" ] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [ "${mi[$VDEC]}" == "ffodivx" ] && [ "${mi[$VCNAME]}" != "MPEG-4" ]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [ "${mi[$VDEC]}" == "ffh264" ]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [ "${mi[$ACODEC]}" == "samr" ] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [ "$adec" == "ffamrnb" ]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Array assignment |
VID_MPLAYER=("${mi[@]}") |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $VID_FFMPEG with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $FUNCNAME $@ |
[ "$FFMPEG" ] || return |
# (AFAIK) Can't use ffmpeg in DVD Mode |
#[ $DVD_MODE -eq 0 ] || return |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert $LINENO "[ $DVD_MODE -eq 0 ] || [ '$DVD_MOUNTP' ]" |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && { |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" |
if [ ! -r "$vfile" ]; then |
error "Failed to locale mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
} |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Video:' | head -1) |
as=$(grep Stream <<<"$FFMPEG_CACHE" | grep 'Audio:' | head -1) |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(grep -o '#0.[0-9]' <<<"$vs" | cut -d'.' -f2) # Video Stream ID |
fi[$VCODEC]=$(egrep -o 'Video: [^,]*' <<<"$vs" | cut -d' ' -f2-) |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(egrep -o 'Audio: [^,]*' <<<"$as" | cut -d' ' -f2) |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | sed 's/^, //' | cut -dx -f1) |
fi[$H]=$(egrep -o ', [0-9]*x[0-9]*' <<<"$vs" | cut -dx -f2) |
# Newer CHANS and some older... |
fi[$CHANS]=$(egrep -o '[0-9]* channels' <<<"$as" | cut -d' ' -f1) |
# ...fallback for older |
if [ -z "${fi[$CHANS]}" ]; then |
local chans=$(egrep -o 'Hz, [^,]*' <<<"$as" | cut -d' ' -f2) |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [ "${fi[$FPS]}" ] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [ "$vsobs" ] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [ -z "${fi[$FPS]}" ]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]* fps' <<<"$vs" | cut -d' ' -f1) |
fi |
# Be consistent with mplayer's output: at least two decimals |
[ "${fi[$FPS]}" ] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(egrep -o 'Duration: [^,]*' <<<"$FFMPEG_CACHE" | cut -d' ' -f2) |
if [ "${fi[$LEN]}" == "N/A" ]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed 's/:/h/' | sed 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# TODO: Replace tail -1 with some better option (see the double DAR example above) |
fi[$ASPECT]=$(egrep -o 'DAR [0-9]*:[0-9]*'<<<"$FFMPEG_CACHE" | tail -1 | cut -d' ' -f2 | sed 's#:#/#') |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[ $DVD_MODE -eq 1 ] && [ "$DVD_DEVICE" ] && fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
VID_FFMPEG=("${fi[@]}") |
} |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local RET_NOLEN=3 RET_NODIM=4 |
[ "$MPLAYER" ] && mplayer_identify "$1" |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[ $DVD_MODE -eq 0 ] && [ "$FFMPEG" ] && ffmpeg_identify "$1" |
[ $DVD_MODE -eq 1 ] && [ "$FFMPEG" ] && [ "$DVD_MOUNTP" ] && ffmpeg_identify "$1" |
# Fail early if none detected length |
[ -z "${VID_MPLAYER[$LEN]}" ] && [ -z "${VID_FFMPEG[$LEN]}" ] && return $RET_NOLEN |
# Classic mode, use both mplayer and ffmpeg when available |
if [ "$MPLAYER" ] && [ "$FFMPEG" ]; then |
# By default take mplayer's values |
VID=("${VID_MPLAYER[@]}") |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[ -z "${VID_MPLAYER[$FPS]}" ] && VID[$FPS]=${VID_FFMPEG[$FPS]} |
[ "${VID_MPLAYER[$FPS]}" ] && [ "${VID_FFMPEG[$FPS]}" ] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${VID_FFMPEG[$FPS]} |
echo $ffps | grep -q '\.[0-9][0-9][0-9]' && VID[$FPS]=$ffps || { |
fptest "${VID_MPLAYER[$FPS]}" -gt 500 && VID[$FPS]=$ffps |
} |
} |
# It doesn't appear to need any workarounds for num. channels either |
[ "${VID_FFMPEG[$CHANS]}" ] && VID[$CHANS]=${VID_FFMPEG[$CHANS]} |
[ "${VID_FFMPEG[$ASPECT]}" ] && VID[$ASPECT]=${VID_FFMPEG[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${VID_FFMPEG[$LEN]} mplen=${VID_MPLAYER[$LEN]} # Shorthands |
[ -z "$fflen" ] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [ $DVD_MODE -eq 0 ] && [ $QUIRKS -eq 0 ]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkex "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $decoder reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
elif [ "$MPLAYER" ]; then |
# Must do with mplayer only... |
VID=("${VID_MPLAYER[@]}") |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[ "${VID[$FPS]}" == "1000.00" ] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[ "${VID[$CHANS]}" == "0" ] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
elif [ "$FFMPEG" ]; then |
# Must do with mplayer only... |
VID=("${VID_FFMPEG[@]}") |
# So far I know of no weird results. Yet. |
else |
assert $LINENO 'false' |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
if [ "$FFMPEG" ]; then |
# FPS at least with two decimals |
if [ $(awkex "int(${VID[$FPS]})") == ${VID[$FPS]} ]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
fi |
local mfps="${VID_MPLAYER[$FPS]}" |
if [ $QUIRKS -eq 0 ] && [ "$MPLAYER" ] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [ $QUIRKS -eq 0 ]; then |
create_temp_dir |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [ $QUIRKS -eq 1 ]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [ -z "${VID[$LEN]}" ]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [ $INTERNAL_MAXREWIND_REACHED -eq 1 ]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[ $reqs -eq 1 ] && reqp=" -WP" || reqp=" -WP$reqs" |
[ $reqs -ge 3 ] && reqs=" -WS" || { # Third try => Recommend -WS |
[ $reqs -eq 1 ] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[ $decoder -eq $DEC_MPLAYER ] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [ $QUIRKS -eq -2 ]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
} |
dump_idinfo() { |
trace $FUNCNAME $@ |
[ "$MPLAYER" ] && echo "Mplayer: $MPLAYER" |
[ "$FFMPEG" ] && echo "FFmpeg: $FFMPEG" |
[ "$MPLAYER" ] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${VID_MPLAYER[$LEN]}) |
Video |
Codec: ${VID_MPLAYER[$VCODEC]} (${VID_MPLAYER[$VCNAME]}) |
Dimensions: ${VID_MPLAYER[$W]}x${VID_MPLAYER[$H]} |
FPS: ${VID_MPLAYER[$FPS]} |
Aspect: ${VID_MPLAYER[$ASPECT]} |
Audio |
Codec: ${VID_MPLAYER[$ACODEC]} (${VID_MPLAYER[$ACNAME]}) |
Channels: ${VID_MPLAYER[$CHANS]} |
============================================== |
EODUMP |
local ffl="${VID_FFMPEG[$LEN]}" |
[ "$ffl" ] && ffl=$(pretty_stamp "$ffl") |
[ -z "$ffl" ] && [ $DVD_MODE -eq 1 ] && ffl="(unavailable in DVD mode)" |
[ "$FFMPEG" ] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${VID_FFMPEG[$VCODEC]} (${VID_FFMPEG[$VCNAME]}) |
Dimensions: ${VID_FFMPEG[$W]}x${VID_FFMPEG[$H]} |
FPS: ${VID_FFMPEG[$FPS]} |
Aspect: ${VID_FFMPEG[$ASPECT]} |
Audio |
Codec: ${VID_FFMPEG[$ACODEC]} (${VID_FFMPEG[$ACNAME]}) |
Channels: ${VID_FFMPEG[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [ "${VID[$ASPECT]}" ]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[ "$xar" ] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $FUNCNAME $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [ -z "$candidates" ]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [ "$DEBUG" -eq 1 ]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:" |
inf "$list" |
fi |
fi |
# Bias towards the Sazanami family |
if grep -qi 'sazanami' <<<"$candidates" ; then |
FONT_MINCHO=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
FONT_MINCHO=$(head -1 <<<"$candidates") |
fi |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
trace $FUNCNAME $@ |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
if [ $decoder -eq $DEC_MPLAYER ] && [ -z "$MPLAYER" ]; then |
inf "No mplayer available. Using ffmpeg only." |
decoder=$DEC_FFMPEG |
elif [ $decoder -eq $DEC_FFMPEG ] && [ -z "$FFMPEG" ]; then |
inf "No ffmpeg available. Using mplayer only." |
decoder=$DEC_MPLAYER |
fi |
if [ $DVD_MODE -eq 1 ] ; then |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
if [ "$MPLAYER" ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
else |
error "DVD mode requires the use of mplayer." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local -a filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Override-able options check, in case they were set from overrides instead |
#+of equivalent command-line options. Will check the actual selected values, |
#+i.e. fail silently if the overrides aren't effective |
[ "$USR_th_height" ] && { check_height "$th_height" || exit $? ; } |
[ "$USR_numcaps" ] && { check_numcaps "$numcaps" || exit $? ; } |
[ "$USR_interval" ] && { check_interval "$interval" || exit $? ; } |
# Interval=0 == default interval |
fptest "$interval" -eq 0 && interval=$DEFAULT_INTERVAL |
sanitise_rename_pattern |
} |
check_height() { # Acceptable height |
if ! is_number "$1" && ! is_percentage "$1" ; then |
error "Height must be a (positive) number or a percentage. Got '$1'." |
return $EX_USAGE |
fi |
} |
check_numcaps() { # Acceptable numcaps |
if ! is_number "$1" ; then |
error "Number of captures must be a (positive) a number! Got '$1'." |
return $EX_USAGE |
fi |
if [ $1 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$1'." |
return $EX_USAGE |
fi |
} |
check_interval() { # Acceptable interval |
if ! get_interval "$1" >/dev/null ; then |
error "Incorrect interval format. Got '$1'." |
return $EX_USAGE |
fi |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$aspect_ratio |
local pre_output_format="$output_format" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
DVD_DEVICE= |
local dvdn=$(realpathr "$f") # dvdn might be a device or an ISO |
if [ -f "$dvdn" ]; then |
# It's an ISO |
DVD_MOUNTP=$(get_dvd_image_mountpoint) |
if [ -z "$DVD_MOUNTP" ]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
elif [ ! -r "$dvdn" ]; then |
# It's something else we cannot read |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_DEVICE="$dvdn" |
DVD_MOUNTP=$(mount | grep -o "^$DVD_DEVICE *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD" |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$DVD_FILE" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
unset dt |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS)" |
fi |
else # Not DVD Mode: |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[ $ecode -eq 0 ] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[ "$UNDFLAG_IDONLY" ] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if is_percentage "$th_height" ; then |
local pc=${th_height/%%/} # BASH %% == RE %$ |
vidcap_height=$(awkex "int ((${VID[$H]} * ${pc}) / 100 + 0.5)") |
inf "Height: $th_height of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
[ "-2" == "$aspect_ratio" ] && [ -z "${VID[$ASPECT]}" ] && aspect_ratio=-1 |
[ "-2" == "$aspect_ratio" ] && [ "${VID[$ASPECT]}" ] && aspect_ratio=0 |
if [ "0" == "$aspect_ratio" ]; then |
if [ "${VID[$ASPECT]}" ]; then |
# Aspect ratio in file headers, honor it |
aspect_ratio=$(awkex "${VID[$ASPECT]}") |
else |
aspect_ratio=$(awkex "${VID[$W]} / ${VID[$H]}") |
fi |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $aspect_ratio." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VIDCAPFILE" ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$hlcapfile" |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mvq "$VIDCAPFILE" "$capfile" |
capfiles=( "${capfiles[@]}" "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 1 ]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[ $DISABLE_SHADOWS -eq 1 ] && [ -z "$HLTIMECODES" ] && dotrim=-trim |
convert -background "$bg_contact" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$DVD_FILE" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# Need a mountpoint to get the actual *title* size |
if [ "$DVD_MOUNTP" ]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Title size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
[ "$wanted_name" ] && \ |
if egrep -q '\.[^\.]+$' <<<"$wanted_name" ; then |
output_format=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$output_format" |
fi |
[ "$wanted_name" ] || wanted_name="$(basename "$f").$output_format" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
let 'FILEIDX++,1' #,1 so that it's always ok |
[ "$UNDFLAG_HANG" ] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
aspect_ratio=$pre_aspect_ratio |
output_format="$pre_output_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [ "$SEQ" ]; then |
ex=$($SEQ 1 10) |
elif [ "$JOT" ]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [ "$ex" ]; then |
exr=$(seqr 1 10) |
if [ "$exr" != "$ex" ]; then |
error "Failed test: seqr() not consistent with external result" |
let 'retval++,1' |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.3576 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(grep -o "[^ ]* #$comm\$"<<<$t | cut -d' ' -f1) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
local inff=inf |
[ "$HAS_COLORS" ] && inff=infplain |
$inff "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2010 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[ -z "$MPLAYER" ] && mpchosen=' [Not available]' |
[ "$MPLAYER" ] && [ $decoder == $DEC_MPLAYER ] && mpchosen=' [Selected]' |
[ -z "$FFMPEG" ] && ffchosen=', Not available' |
[ "$FFMPEG" ] && [ $decoder == $DEC_FFMPEG ] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[ "$showlong" ] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable=\"\$SOME_VAR\"-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for random \"values\": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[ "$showlong" ] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomizes colours and fonts." |
[ -z "$showlong" ] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:V:R:Z:o:P: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:,randomsource:,undocumented:,output:,fullhelp,profile:,"\ |
"jpeg2,nonlatin" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
check_interval "$2" |
interval=$(get_interval "$2") |
timecode_from=$TC_INTERVAL |
USR_interval=$interval |
USR_timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_numcaps "$2" |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
USR_numcaps="$2" |
USR_timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]="$2" |
shift ;; |
-u|--username) user="$2" ; USR_user="$user" ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; USR_title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_fromtime="$fromtime" |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_end_offset="$end_offset" |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$totime" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_totime=$totime |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( "${initial_stamps[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
output_format=jp2 |
USR_output_format=jp2 |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
USR_output_format="$output_format" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
--shoehorn) |
warn "$1 is deprecated, please use '--undocumented shoehorn=\"$2\"' instead" |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ; USR_decoder=$decoder ;; |
-M) decoder=$DEC_MPLAYER ; USR_decoder=$decoder ;; |
-H|--height) |
check_height "$2" |
th_height="$2" |
USR_th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
USR_aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ; USR_aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
USR_cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
USR_extended_factor=$extended_factor |
shift |
;; |
--mincho) |
warn "--mincho is deprecated, please use -Ij or --nonlatin instead" |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
if [ ${#2} -gt 1 ]; then |
# j=, k= syntax |
FONT_MINCHO="${2:2}" |
USR_FONT_MINCHO="$FONT_MINCHO" |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
# If the user didn't pick one, try to select automatically |
if [ -z "$USR_FONT_MINCHO" ]; then |
set_extended_font |
inf "Filename font set to '$FONT_MINCHO'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
override "$2" "command line" |
fi |
shift |
;; |
-W) |
case "$2" in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[ "$UNDFLAG_NOPREFIX" ] && plain_messages=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkex "$QUIRKS_MAX_REWIND * (2^$n)") |
let 'INTERNAL_WS_C+=n,1' |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP / (2^$n)") |
let 'INTERNAL_WP_C+=n,1' |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[ ${#2} -gt 1 ] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkex "$QUIRKS_LEN_STEP * (2^$n)") |
let 'INTERNAL_WP_C-=n,1' |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-P|--profile) |
case "$2" in |
classic) # Classic colour scheme |
bg_heading=YellowGreen bg_sign=SlateGray bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
bg_heading=YellowGreen bg_sign=SandyBrown bg_contact=White |
bg_title=White fg_heading=Black fg_sign=Black |
;; |
esac |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [ $HPAD -ne 0 ] ; then |
inf "Padding disabled." # Kinda... |
HPAD=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-2 # Special value: Auto detect only if ffmpeg couldn't |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$FFMPEG"' |
warn "[U] FFMPEG=$FFMPEG" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert $LINENO 'test -x "$MPLAYER"' |
warn "[U] MPLAYER=$MPLAYER" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG='' |
warn "FFmpeg disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_MPLAYER |
;; |
disable_mplayer) |
MPLAYER='' |
warn "Mplayer disabled" |
assert $LINENO "[ '$MPLAYER' ] || [ '$FFMPEG' ]" |
decoder=$DEC_FFMPEG |
;; |
# This is an old option from the first versions when the script |
# failed a lot more, I haven't used it for years and I don't think |
# anyone would need it anymore but I'll keep it at least for |
# a few more versions |
shoehorn=*) |
shoehorned="$(cut -d'=' -f2-<<<"$2")" |
;; |
*) false ;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then |
[ $decoder -eq $DEC_MPLAYER ] && d='mplayer' |
[ $decoder -eq $DEC_FFMPEG ] && d='ffmpeg' |
infplain '[ svn $Rev$ ]' |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER |
FFMPEG: $FFMPEG |
AWK: $(type -pf awk) |
Filterchain: [ ${FILTERS_IND[*]} ] |
Decoder: $d |
Safe step: $QUIRKS_LEN_STEP |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version) |
EOD |
# FIXME: Any portable way to print AWK version? |
exit |
fi |
DEBUG=1 |
inf "Testing internal consistency..." |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/rpm-package/vcs.spec.in |
---|
0,0 → 1,97 |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: 1%{?disttag},upstream |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 2.05b |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
%install |
make DESTDIR=%buildroot install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Manpage |
#%{_mandir}/man1/%{name}.1.gz |
%doc CHANGELOG |
%changelog |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/rpm-package/Makefile |
---|
0,0 → 1,33 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
VERSION:=$(shell head -50 vcs | grep 'declare -r VERSION=' | perl -pe 's/.*"(.*)".*/\1/') |
#PACKAGER=$(shell grep ^`id -nu` /etc/passwd | cut -d: -f5 | cut -d, -f1) |
all: |
@echo 'There'\''s nothing to be built' |
@echo 'Available make commands:' |
@echo '* Build RPM (and generate spec)' |
@echo ' $$ make rpm' |
@echo '* Generate spec file' |
@echo ' $$ make spec' |
@echo '* Install' |
@echo ' $$ make install' |
clean: |
$(RM) vcs.spec |
spec: vcs.spec.in |
test "$(VERSION)" |
@test "$(PACKAGER)" || echo "No PACKAGER set!" |
@test "$(PACKAGER)" |
cat vcs.spec.in | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > vcs.spec |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean spec |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 2.05b), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/debian/changelog |
---|
0,0 → 1,44 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.11/debian-package/Makefile |
---|
0,0 → 1,14 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
all: |
clean: |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/Makefile |
---|
0,0 → 1,63 |
#!/usr/bin/make -f |
# $Id$ |
VER=$(shell grep VERSION vcs | head -n1 | sed 's/\#.*//' | sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
check-no-svn: |
@if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo 'RELEASE is set to 0!' ; false ; fi |
prep: |
cp vcs CHANGELOG debian-package/ |
cp vcs rpm-package/ |
dist: check-rel check-no-svn prep gz bz2 plaintext changelog deb rpm cleanup |
gz: |
cp vcs vcs-$(VER) |
chmod -x vcs-$(VER) |
gzip -9 vcs-$(VER) |
bz2: |
cp vcs vcs-$(VER) |
chmod -x vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
plaintext: |
cp vcs vcs-$(VER) |
chmod -x vcs-$(VER) |
changelog: |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
cleanup: |
$(RM) vcs Makefile *.changes |
$(RM) -r debian-package |
$(RM) -r rpm-package |
deb: |
cd debian-package/ && dpkg-buildpackage -rfakeroot -us -uc -b |
rpm: vcs.spec |
mkdir rpm-package/vcs-$(VER)/ |
cp vcs CHANGELOG rpm-package/Makefile rpm-package/vcs-$(VER)/ |
mv vcs.spec rpm-package/vcs-$(VER)/ |
cd rpm-package && tar zcvf vcs-$(VER).tar.gz vcs-$(VER) |
$(RM) vcs.spec |
$(RM) -r rpm-package/vcs-$(VER) |
cd rpm-package && fakeroot rpmbuild -tb vcs-$(VER).tar.gz |
-ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . |
$(RM) rpm-package/vcs-$(VER).tar.gz |
vcs.spec: rpm-package/vcs.spec.in |
cd rpm-package && $(MAKE) -f Makefile spec PACKAGER="$(PACKAGER)" |
mv rpm-package/vcs.spec . |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11/CHANGELOG |
---|
0,0 → 1,312 |
1.11: (2010-03-07) |
* FEAT: Allow setting output filename. With extension will set output |
format, without will inherit it. |
* FEAT: Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
* FEAT: Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase |
further. Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of |
broken files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
* FEAT: Allow percentages in height. |
* BUGFIX: Don't pass ms to mplayer. It ignores them anyway and in some |
rare cases breaks the last capture (possibly due to the 5-frames |
hack) |
* BUGFIX: Honor detected aspect ratio if found |
* BUGFIX: Try to detect files that might fail on the last capture and |
trigger safe mode |
* BUGFIX: Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems. |
* BUGFIX: Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl. It's required by POSIX anyway |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: assert() |
* INTERNAL: Cleanup: correctness checks converted to asserts, removal |
of old dead code, fixed typos in comments |
* Fixed various typos and cut out lines in CHANGELOG |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.11 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.0.100a/Makefile |
---|
0,0 → 1,41 |
#!/usr/bin/make -f |
# $Id$ |
VER=$(shell grep VERSION vcs|head -n1|sed 's/#.*//'|sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
check-no-svn: |
if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
prep: |
cp vcs CHANGELOG debian-package/ |
chmod -x vcs |
dist: check-no-svn prep gz bz2 plaintext changelog deb cleanup |
gz: |
cp vcs vcs-$(VER) |
gzip -9 vcs-$(VER) |
bz2: |
cp vcs vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
plaintext: |
mv vcs vcs-$(VER) |
changelog: |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
cleanup: |
$(RM) -i Makefile *.changes |
$(RM) -r debian-package |
deb: |
cd debian-package/ && dpkg-buildpackage -rfakeroot -us -uc -b |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.100a/CHANGELOG |
---|
0,0 → 1,222 |
1.0.100a: (2009-04-10) ("1.1.0 RC2") |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) ("1.1.0 RC") |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe. |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added my IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
1.0.9a: (2007-06-10) (-Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/debian/changelog |
---|
0,0 → 1,33 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bc, bash, grep, imagemagick (>= 6.0), mktemp, mplayer, ffmpeg, gsfonts |
Recommends: lsdvd |
Description: vcs is a script that creates a contact sheet (preview) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/debian/copyright |
---|
0,0 → 1,37 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
# Please also look if there are files or directories which have a |
# different copyright/license attached and list them here. |
/ATTIC/video-contact-sheet/tags/1.0.100a/debian-package/Makefile |
---|
0,0 → 1,14 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
all: |
clean: |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.100a/vcs |
---|
0,0 → 1,2912 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# References: |
# Mainly pages I've taken snippets from or wrote code based on them; or pages |
# containing reference/technical data |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# [M1] "[MEncoder-users] Thumbnail creation" |
# <http://lists.mplayerhq.hu/pipermail/mencoder-users/2006-August/003843.html> |
# [VC1] VC-1 and derived codecs information |
# <http://wiki.multimedia.cx/index.php?title=VC-1> |
# [FJ] GNU seq’s cousin on FreeBSD is... jot |
# <http://www.nevdull.com/2007/09/24/gnu-seqs-cousin-on-freebsd-is-jot/> |
# [FNL] Re: Replacing spaces with newlines using awk: msg#00064 |
# <http://osdir.com/ml/editors.sed.user/2004-07/msg00064.html> |
# [FD1] File Descriptors in Bourne shell (sh,ksh,bash). |
# <http://mixedvolume.blogspot.com/2004/12/file-descriptors-in-bourne-shell.html> |
# [FD2] Redirection [[Bash Hackers Wiki] |
# <http://bash-hackers.org/wiki/doku.php/syntax/redirection> |
# |
declare -r VERSION="1.0.100a" # ("1.1.0 RC2") |
# {{{ # CHANGELOG |
# History (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.100a: (Focus on FreeBSD support -and hopefully better POSIX compatibility-) |
# * FEATURE: FreeBSD (7.1-RELEASE) support |
# * COMPATIBILITY: |
# - Call bash through env |
# - Ensure we're using the correct getopt version |
# - Try to use POSIX sed options when appropriate |
# - Replaced incompatible sed constructs |
# - Use mktemp's common GNU/BSD(/POSIX?) syntax |
# - Use jot instead of seq if required and available |
# * BUGFIX: Don't fail if tput is unable to change colours |
# * BUGFIX: Check for requirements before anything else |
# * INTERNAL: Cache tput output |
# * FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
# also to repeat a set of results and compare outputs on different |
# systems |
# * Corrected info message in photos mode |
# |
# }}} # CHANGELOG |
set -e |
# This might change the way some commands are used. |
# Right now it's unused |
#declare -i IS_GNU=1 |
#grep -qi gnu <<<"$OSTYPE" || IS_GNU=0 |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# * Better DVD support (e.g. real detection of aspect ratio) |
# * Use ffmpeg's detected length if shorter than mplayer's |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# This is the horizontal padding added to each capture. Changing it might break |
# extended set's alignement so keep this in mind if you tinker with it |
# When shadows are enabled, 5 is substracted from this value in csheet_montage |
# so keep this in mind! |
declare -ri HPAD=8 |
# These are used as constants but will be set from the available system |
# programs |
# ERESED # see choose_eresed |
# SEQ # see choose_seqw |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
declare -i multiple_input_files=0 |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
declare FFMPEG_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare prefix_err= prefix_inf= prefix_warn= suffix_fback= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
# Note GETOPT doesn't make sense to be overridden from the command-line |
'GETOPT' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf ) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEFAULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really work anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$($ERESED 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$($ERESED 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == "$(bc -q <<<"$1 $op $3")" ] |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl($1 = string) |
stonl() { |
# Not pretty, but seems to be standard ('\n' works in GNU |
# but not in e.g. FreeBSD) |
sed 's/ /\ |
/g' <<<"$1" |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v="$1" |
local -i hv=15031 |
local c= |
if [ "$v" ]; then # seqw 0 0 would be catastrophic if SEQ==jot |
for i in $(seqw 0 $(( ${#v}-1 ))); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$($ERESED 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$($ERESED 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$($ERESED 's/([0-9])\./\1 + ./g' <<<"$t") # seconds followed by ms, with no "S" |
t=$($ERESED 's/\.\.+/./g'<<<$t) |
t=$($ERESED 's/\.([0-9]+)/0.\1 + /g' <<<$t) |
t=$($ERESED 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s ms |
if grep -q '\.' <<<"$t" ; then |
# Have ms |
s=$(cut -d'.' -f1 <<<$t) |
ms=$(cut -d'.' -f2 <<<$t) |
else |
s=$t |
ms=0 |
fi |
local R="" |
if [ $h -gt 0 ]; then |
R="$h:" |
# Unreproducible bug reported by wdef: Minutes printed as hours |
# fixed with "else R="00:"" |
fi |
R="$R$(pad 2 "$m"):$(pad 2 $s)" |
# Milliseconds, only supported by ffmpeg, not printed otherwise |
if [ $decoder -eq $DEC_FFMPEG ]; then |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R="$R.$ms" |
fi |
# Trim (most) decimals |
$ERESED 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$($ERESED 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$($ERESED 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# At least the busybox implementation is a real world du in usage that doesn't allow |
# using --bytes. Note that using "ls -H" is not an option either for the same reason. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Gets the size of a block device |
get_blockdev_size() { |
# This is something I've never done so I'm still looking for the right |
# way to do it portably. |
# Alternatives: |
# * fdisk -s (no need for privileged access, read-only) |
# Prints the number of blocks (only in GNU's version?, FreeBSD's |
# doesn't, so probably POSIX in general doesn't either). |
# In Linux blocks are always 1024 AFAICT, but I'm not sure about |
# other unices (the -in disk- block size for DVDs is 2048). |
# * hal |
# hal-find-by-property --key block.device --string <(REAL)DEVICE> |
# hal-get-property --udi <DEVICEID> --key volume.disc.capacity |
# Gets byte size but HAL is far from standard (only Linux |
# and FreeBSD have it AFAIK. DBUS, on which it relies, wasn't |
# enabled byb default on my FreeBSD install). |
# FreeBSD has no block devices either. |
# * sysfs |
# cat /sys/block/<(KERNEL)DEVICE>/size |
# Size is given in sectors (512 blocks). Linux only. *BSD has |
# sysctl of which I've no clue. |
local dev="$1" |
# Only GNU systems with block devices are compatible with the current code |
if [ ! -b "$1" ] || grep -q gnu <<<"$OSTYPE" ; then |
echo "?" |
return |
fi |
local numblocks=$(/sbin/fdisk -s "$dev" 2>"$stderr") |
# FIXME: When fdisk is replaced by a better alternative this should go away |
if is_number "$numblocks" ; then |
get_pretty_size $(( 1024 * $numblocks )) |
else |
echo "?" |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut $SEQ ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi >/dev/null |
done |
# TODO: [x2] |
return $retval |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf $GETOPT ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [ $gor -eq 4 ]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [ $gor -ne 4 ]; then |
error "No compatible version of getopt in path, can't continue." |
error " For details on how to correct this problems, see <http://p.outlyer.net/vcs#getopt>" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Set the correct argument to pass to sed |
# The argument to enable extended regular expressions is different in GNU (-r) |
# and POSIX (-E), try to detect it |
choose_eresed() { |
if [ "a" == "$(sed -r 's/A/a/' 2>/dev/null<<<'A')" ]; then |
ERESED='sed -r' |
elif [ "a" == "$(sed -E 's/A/a/' 2>/dev/null<<<'A')" ]; then |
ERESED='sed -E' |
else |
error "The version of sed in the system is not supported. |
Please, contact the author" |
return $EX_SOFTWARE |
fi |
} |
# Choose seq or jot, fail if none is present |
# The actual program is wrapped in seqw() |
choose_seqw() { |
if type -pf seq ; then |
SEQ='seq' |
elif type -pf jot ; then |
SEQ='jot' |
else |
error "Either seq or jot are required" |
return $EX_UNAVAILABLE |
fi >/dev/null |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [ "$RANDFUNCTION" == "filerand" ]; then |
7<&- # Close FD 7 |
fi |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
[ $plain_messages -eq 0 ] && echo -n $prefix_err |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1$suffix_fback" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
[ $plain_messages -eq 0 ] && echo -n $prefix_warn |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
[ $plain_messages -eq 0 ] && echo -n $prefix_inf |
echo "$1$suffix_fback" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# |
# Initialises the variables affecting coloured feedback |
init_feedback() { |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
if ! type -pf tput >/dev/null ; then |
# XXX: Are nested functions supported by older versions? |
tput() { cat >/dev/null <<<"$1"; } |
elif ! tput bold || # If tput can't tinker with the color, no need to continue |
! tput setaf 0 >/dev/null || |
! tput sgr0 ; |
then |
prefix_err= prefix_inf= prefix_warn= |
suffix_fback= |
else # tput doesn't fail to change colors |
prefix_err=$(tput bold; tput setaf 1) |
prefix_warn=$(tput bold; tput setaf 3) |
prefix_inf=$(tput bold; tput setaf 2) |
suffix_fback=$(tput sgr0) |
fi |
} |
# |
# seq wrapper/replacement |
# seq($1 = from, $2 = to) |
seqw() { |
if [ "$SEQ" == "seq" ]; then |
seq $1 $2 |
elif [ "$SEQ" == "jot" ]; then |
jot $(( 1 + $2 - $1 )) $1 |
else |
error "'$SEQ' is not supported, please change the value of \$SEQ" |
return $EX_SOFTWARE |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[ "$TMPDIR" ] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $(rand) % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
# TODO: Is there a standard for PAL yet? |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss $stamp $shoehorned -dvd-device "$DVD_FILE" \ |
"dvd://$DVD_TITLE" |
else |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss $stamp $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
if [ $pts -le 8 ]; then |
pts=8 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Beware, using very small timestamps to accomodate smaller captures,\ |
you might prefer using -dt to disable them" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=$(( $HPAD-5 )) |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=4 |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# With the shadows moved to a filter, there's not enough spacing to header |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqw 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqw 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why I replace spaces by newlines |
local s=$1 |
stonl "$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>/dev/null | grep ^ID) |
# Used as fallback. Introduced in 1.0.99 so expect it to fail :P |
FFMPEG_CACHE=$(ffmpeg -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | grep Stream) |
else |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>/dev/null | grep ^ID) |
fi |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
if [ $DVD_MODE -eq 0 ]; then |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
else |
VID[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
fi |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Fallback for values known to fail often |
if [ "$FFMPEG_CACHE" ]; then |
# FPS=1000.00 happens often for WMV |
if [ "${VID[$FPS]}" == "1000.00" ]; then |
local fps2=$(grep Stream <<<"$FFMPEG_CACHE" | grep Video | head -1 | \ |
egrep -o ', [0-9]+\.[0-9]+ ' | egrep -o '[0-9]+.*[0-9]') |
if is_float "$fps2" ; then |
VID[$FPS]=$fps2 |
fi |
unset fps2 |
fi |
# Number of channels 0 happens for WMA in non-x86 |
# Mplayer seems to default to 2 for >2, so ffmpeg might be a better default option |
# if [ "${VID[$CHANS]}" ] && ( ! is_number "${VID[$CHANS]}" || [ ${VID[$CHANS]} -eq 0 ] ) ; then |
local ch2=$(grep Stream <<<"$FFMPEG_CACHE" | grep Audio | head -1 | cut -d, -f3 | sed 's/^ //') |
if [ "$ch2" ]; then |
case $ch2 in |
mono) VID[$CHANS]=1 ;; |
stereo) VID[$CHANS]=2 ;; |
5.1) VID[$CHANS]=6 ;; |
*) ;; |
esac |
fi |
# fi |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $DVD_MODE -eq 1 ] ; then |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts+=( $filter ) |
filts+=( filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts+=( $filter ) |
fi |
done |
FILTERS_IND=( ${filts[*]} ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts+=( "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts+=( "$filter" ) ;; |
esac |
done |
FILTERS_IND=( ${filts[*]} ${end_filts[*]} ) |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
local dvdn="$f" |
if [ -b "$dvdn" ]; then |
dvdn="DVD" |
elif [ -c "$dvdn" ]; then |
if grep -q bsd <<<"$OSTYPE"; then |
inf "Warning: DVD support is even more experimental in *BSD" |
else |
warn "DVD device is a character device" |
fi |
dvdn="DVD" |
elif [ ! -f "$dvdn" ]; then |
error "File \"$dvdn\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
inf "Using DVD Title #$DVD_TITLE" |
fi |
else |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $($ERESED 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
local is_dev=0 |
( test -b "$DVD_FILE" || test -c "$DVD_FILE" ) && is_dev=1 |
local dvd_label=$(lsdvd "$DVD_FILE" | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
if [ $is_dev -eq 1 ]; then # This is a real DVD, not an iso |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Disc size" |
filesize_value="$(get_blockdev_size "$DVD_FILE")" |
else |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test(). Running with -D triggers this. |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30s 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$($ERESED "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$($ERESED "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2009 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroidframe": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
"photos": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
-R <file> |
--randomsource <file> Use the provided file as a source for random "values": |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use using randomisation (e.g. the |
"photos" and "polaroid" modes). |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# Important to do this before any message can be thrown |
init_feedback |
# Adjust sed for POSIX/GNU compatibility |
choose_eresed |
# Adjust seq for POSIX/GNU compatibility |
choose_seqw |
# Ensure $GETOPT is Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs || exit $EX_UNAVAILABLE |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$("$GETOPT" -s bash -o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I::k:W:E:d:V:R: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:,randomsource:,undocumented:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
if grep -q 'GETOPT=' <<<"$2" ; then |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "GETOPT can't be overridden from the command line." |
else |
override "$2" "command line" |
fi |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to photos funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND+=( 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-R|--randomsource) |
if [ ! -r "$2" ]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD Support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-1 |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
*) false ;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
DEBUG=1 |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.100a |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/Makefile |
---|
0,0 → 1,14 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
all: |
clean: |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/debian/changelog |
---|
0,0 → 1,27 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bc, bash, grep, imagemagick (>= 6.0), mktemp, mplayer, ffmpeg, gsfonts |
Recommends: lsdvd |
Description: vcs is a script that creates a contact sheet (preview) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.0.99/debian-package/debian/copyright |
---|
0,0 → 1,37 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
# Please also look if there are files or directories which have a |
# different copyright/license attached and list them here. |
/ATTIC/video-contact-sheet/tags/1.0.99/CHANGELOG |
---|
0,0 → 1,205 |
1.0.99: (2009-03-11) ("1.1.0 RC") |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe. |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added my IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
1.0.9a: (2007-06-10) (-Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.99/vcs |
---|
0,0 → 1,2672 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# References: |
# Mainly pages I've taken snippets from or wrote code based on them; or pages |
# containing reference/technical data |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# [M1] "[MEncoder-users] Thumbnail creation" |
# <http://lists.mplayerhq.hu/pipermail/mencoder-users/2006-August/003843.html> |
# [VC1] VC-1 and derived codecs information |
# <http://wiki.multimedia.cx/index.php?title=VC-1> |
# |
declare -r VERSION="1.0.99" # ("1.1.0 RC") |
# {{{ # CHANGELOG |
# History (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.99: (2009-3-11) |
# * FEATURE: Experimental support for DVDs (-V) |
# * FEATURE: Added JPEG 2000 output format (-j2) |
# * FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
# older version is now renamed as simply 'photos' |
# New "funky" modes: newer polaroid, photos (older polaroid), |
# polaroidframe. |
# * Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to |
# 1 to disable) |
# * BUGFIX/COSMETIC: Re-added the missed space before filename |
# * BUGFIX/COSMETIC: Reworked alignment and padding |
# * Timestamps size is adjusted with smaller captures |
# * BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the |
# same position (reported by Aleksandar Urošević, formerly |
# unreproducible) |
# * Better detection of video/audio features by falling back to ffmpeg when |
# appropriate |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# * Better DVD support (e.g. real detection of aspect ratio) |
# * Use ffmpeg's detected length if shorter than mplayer's |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# This is the horizontal padding added to each capture. Changing it might break |
# extended set's alignement so keep this in mind if you tinker with it |
# When shadows are enabled, 5 is substracted from this value in csheet_montage |
# so keep this in mind! |
declare -ri HPAD=8 |
# }}} # End of constants |
# {{{ # Override-able variables |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare bg_tstamps='#000000aa' # Background for the timestamps box |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
declare -i DVD_MODE=0 DVD_TITLE=1 |
declare DVD_FILE= |
declare -i multiple_input_files=0 |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
declare FFMPEG_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
'DISABLE_.*' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEFAULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == "$(bc -q <<<"$1 $op $3")" ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/([0-9])\./\1 + ./g' <<<"$t") # seconds followed by ms, with no "S" |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/\.([0-9]+)/0.\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s ms |
if grep -q '\.' <<<"$t" ; then |
# Have ms |
s=$(cut -d'.' -f1 <<<$t) |
ms=$(cut -d'.' -f2 <<<$t) |
else |
s=$t |
ms=0 |
fi |
local R="" |
if [ $h -gt 0 ]; then |
R="$h:" |
# Unreproducible bug reported by wdef: Minutes printed as hours |
# fixed with "else R="00:"" |
fi |
R="$R$(pad 2 "$m"):$(pad 2 $s)" |
# Milliseconds, only supported by ffmpeg, not printed otherwise |
if [ $decoder -eq $DEC_FFMPEG ]; then |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R="$R.$ms" |
fi |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# At least the busybox implementation is a real world du in usage that doesn't allow |
# using --bytes. Note that using "ls -H" is not an option either for the same reason. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Gets the size of a block device |
get_blockdev_size() { |
# This is something I've never done so I'm still looking for the right |
# way to do it portably. |
# Alternatives: |
# * fdisk -s (no need for privileged access, read-only) |
# Prints the number of blocks. In Linux they're always 1024 AFAICT, |
# but I'm not sure about other unices (the -in disk- block size for |
# DVDs is 2048). |
# * hal |
# hal-find-by-property --key block.device --string <(REAL)DEVICE> |
# hal-get-property --udi <DEVICEID> --key volume.disc.capacity |
# Gets byte size but HAL is far from standard (only Linux |
# and FreeBSD have it AFAIK) |
# * sysfs |
# cat /sys/block/<(KERNEL)DEVICE>/size |
# Size is given in sectors (512 blocks). Linux only. *BSD has |
# sysctl of which I've no clue. |
local dev="$1" |
local numblocks=$(/sbin/fdisk -s "$dev") |
get_pretty_size $(( 1024 * $numblocks )) |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[ "$filter" == "$ref" ] || continue |
return 0 |
done |
return 1 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
# TODO: Is there a standard for PAL yet? |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
{ |
if [ $DVD_MODE -eq 1 ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss $stamp $shoehorned -dvd-device "$DVD_FILE" \ |
"dvd://$DVD_TITLE" |
else |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss $stamp $shoehorned "$f" |
fi |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" "$5" "$6" ) -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$pts_tstamps |
if [ $height -lt 200 ]; then |
pts=$(( $pts_tstamps / 3 )) |
elif [ $height -lt 400 ]; then |
pts=$(( $pts_tstamps * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
if [ $pts -le 8 ]; then |
pts=8 |
if [ $index -eq 1 ] && [ $context -ne $CTX_EXT ]; then |
warn "Beware, using very small timestamps to accomodate smaller captures,\ |
you might prefer using -dt to disable them" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
echo -n " \( -box '$bg_tstamps' -fill '$fg_tstamps' -pointsize '$pts' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe0() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
echo -n "\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[ $border -lt 7 ] || border=6 |
echo -n "-bordercolor white -mattecolor white -frame ${border}x${border} " |
# FIXME: This is rather ugly (double-flipping) there's sure a better way |
echo -n "\( -flip -splice 0x$(( $border*5 )) \) " |
echo "-flip -bordercolor grey60 -border 1 +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=$(( $HPAD-5 )) |
vpad=0 |
splice=5x10 |
else |
hpad=$HPAD |
vpad=4 |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# With the shadows moved to a filter, there's not enough spacing to header |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $RANDOM % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
if [ $DVD_MODE -eq 0 ]; then |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>/dev/null | grep ^ID) |
# Used as fallback. Introduced in 1.0.99 so expect it to fail :P |
FFMPEG_CACHE=$(ffmpeg -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | grep Stream) |
else |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device $DVD_FILE dvd://$DVD_TITLE \ |
2>/dev/null | grep ^ID) |
fi |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
if [ $DVD_MODE -eq 0 ]; then |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
else |
VID[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
fi |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Fallback for values known to fail often |
if [ "$FFMPEG_CACHE" ]; then |
# FPS=1000.00 happens often for WMV |
if [ "${VID[$FPS]}" == "1000.00" ]; then |
local fps2=$(grep Stream <<<"$FFMPEG_CACHE" | grep Video | head -1 | \ |
egrep -o ', [0-9]+\.[0-9]+ ' | egrep -o '[0-9]+.*[0-9]') |
if is_float "$fps2" ; then |
VID[$FPS]=$fps2 |
fi |
unset fps2 |
fi |
# Number of channels 0 happens for WMA in non-x86 |
# Mplayer seems to default to 2 for >2, so ffmpeg might be a better default option |
# if [ "${VID[$CHANS]}" ] && ( ! is_number "${VID[$CHANS]}" || [ ${VID[$CHANS]} -eq 0 ] ) ; then |
local ch2=$(grep Stream <<<"$FFMPEG_CACHE" | grep Audio | head -1 | cut -d, -f3 | sed 's/^ //') |
if [ "$ch2" ]; then |
case $ch2 in |
mono) VID[$CHANS]=1 ;; |
stereo) VID[$CHANS]=2 ;; |
5.1) VID[$CHANS]=6 ;; |
*) ;; |
esac |
fi |
# fi |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Checks if the provided arguments make sense and are allowed to be used |
# together |
coherence_check() { |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# Currently it's not allowed to use dvd mode with more than one input |
# "file" (in this mode, input files are actually dvd titles of the file |
# provided in -V) |
if [ $DVD_MODE -eq 1 ] ; then |
if [ $multiple_input_files -eq 1 ]; then |
error "Only an input file is allowed in DVD mode" |
return $EX_UNAVAILABLE |
fi |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [ $decoder -ne $DEC_MPLAYER ]; then |
warn "DVD mode requires the use of mplayer, falling back to it" |
decoder=$DEC_MPLAYER |
fi |
fi |
local filter= |
if [ $DISABLE_TIMESTAMPS -eq 0 ] && |
local filts=( ) |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [ "$filter" == "filt_polaroid" ]; then |
filts+=( $filter ) |
filts+=( filt_apply_stamp ) |
elif [ "$filter" == "filt_apply_stamp" ]; then |
continue; |
else |
filts+=( $filter ) |
fi |
done |
FILTERS_IND=( ${filts[*]} ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [ $DISABLE_SHADOWS -ne 1 ]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [ $DISABLE_TIMESTAMPS -ne 1 ]; then |
filts+=( "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts+=( "$filter" ) ;; |
esac |
done |
FILTERS_IND=( ${filts[*]} ${end_filts[*]} ) |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
# XXX: Some of this should be moved to coherence_check |
if [ $DVD_MODE -eq 1 ]; then # DVD Mode |
f="$DVD_FILE" |
local dvdn="$f" |
if [ -b "$dvdn" ]; then |
dvdn="DVD" |
elif [ ! -f "$dvdn" ]; then |
error "File \"$dvdn\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
if ! is_number "$1" ; then |
error "DVD Title must be a number (e.g.: \$ vcs -V /dev/dvd 1)" |
exit $EX_USAGE |
fi |
DVD_TITLE=$1 |
if [ $DVD_TITLE -eq 0 ]; then |
local dt="$(lsdvd "$DVD_FILE" | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
inf "Using DVD Title #$DVD_TITLE" |
fi |
else |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File $VIDCAPFILE exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h $CTX_EXT $n || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
if [ "$HLTIMECODES" ]; then |
local hlw=$(imw "$hlfile") |
if [ $hlw -gt $width ]; then width=$hlw ; fi |
fi |
if [ "$extended_factor" != "0" ]; then |
local exw=$(imw $extoutput) |
if [ $exw -gt $width ]; then width=$exw ; fi |
fi |
fi |
if [ $csw -lt $width ]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
convert \( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) "$output" \ |
\( -size $(( ($width - $csw) / 2 ))x$csh xc:transparent \) +append "$output" |
unset csh |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [ $hlw -lt $width ]; then |
convert \( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) "$hlfile" \ |
\( -size $(( ($width - $hlw) / 2 ))x$hlh xc:transparent \) +append "$hlfile" |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [ $exw -lt $width ]; then |
# Expand the extended set to be the correct size |
convert \( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) "$extoutput" \ |
\( -size $(( ($width - $exw) / 2 ))x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [ $DVD_MODE -eq 1 ]; then |
local is_dev=0 |
test -b "$DVD_FILE" && is_dev=1 |
local dvd_label=$(lsdvd "$DVD_FILE" | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
if [ $is_dev -eq 1 ]; then # This is a real DVD, not an iso |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Disc size" |
filesize_value="$(get_blockdev_size "$DVD_FILE")" |
else |
filename_value="$(basename "$DVD_FILE") $filename_value (DVD Label: $dvd_label)" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$font_heading" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30s 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007-2009 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-V|--dvd <file.iso|dvd_device> |
DVD Mode, use file.iso as DVD. In this mode the |
<file> argument must point to the title number, e.g.: |
$ vcs -V somedvd.iso 1 |
Passing title 0 will use the default (longest) title. |
$ vcs -V /dev/dvd 0 |
Implies -A (auto aspect ratio) |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg 2 Output in jpeg 2000 |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroidframe": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each image. |
"photos": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I::k:W:E:d:V: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:,dvd:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) |
if [ "$2" ]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [ "$2" != "2" ] && [ "$2" != "2000" ]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
output_format=jp2 |
else |
output_format=jpg |
fi |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
FILTERS_IND+=( 'filt_polaroid ') |
grav_timestamp=South |
fg_tstamps=Black |
bg_tstamps=Transparent |
pts_tstamps=$(( $pts_tstamps * 3 / 2 )) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [ $DISABLE_TIMESTAMPS -eq 0 ]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-V|--dvd) |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD Support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
DVD_FILE="$2" |
decoder=$DEC_MPLAYER |
aspect_ratio=-1 |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
DEBUG=1 |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
elif [ "$2" ]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.99/Makefile |
---|
0,0 → 1,41 |
#!/usr/bin/make -f |
# $Id$ |
VER=$(shell grep VERSION vcs|head -n1|sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
check-no-svn: |
if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
prep: |
cp vcs CHANGELOG debian-package/ |
chmod -x vcs |
dist: check-no-svn prep gz bz2 plaintext changelog deb cleanup |
gz: |
cp vcs vcs-$(VER) |
gzip -9 vcs-$(VER) |
bz2: |
cp vcs vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
plaintext: |
mv vcs vcs-$(VER) |
changelog: |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
cleanup: |
$(RM) -i Makefile *.changes |
$(RM) -r debian-package |
deb: |
cd debian-package/ && dpkg-buildpackage -rfakeroot -us -uc -b |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.99 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR="$(CURDIR)/debian/vcs" install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Package: vcs |
Architecture: all |
Depends: bc, bash, grep, imagemagick (>= 6.0), mktemp, mplayer, ffmpeg, gsfonts |
Description: vcs is a script that creates a contact sheet (preview) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
. |
Upstream homepage <http://p.outlyer.net/vcs/>. |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/debian/changelog |
---|
0,0 → 1,18 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/debian/copyright |
---|
0,0 → 1,37 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
# Please also look if there are files or directories which have a |
# different copyright/license attached and list them here. |
/ATTIC/video-contact-sheet/tags/1.0.12/debian-package/Makefile |
---|
0,0 → 1,14 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
all: |
clean: |
install: |
install -d "$(DESTDIR)$(prefix)/bin" |
install -m755 -o0 -g0 vcs "$(DESTDIR)$(prefix)/bin/" |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.12/CHANGELOG |
---|
0,0 → 1,189 |
1.0.12: (2008-04-16) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added my IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
1.0.9a: (2007-06-10) (-Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.12/vcs |
---|
0,0 → 1,2297 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# References: |
# Mainly pages I've taken snippets from or wrote code based on them; or pages |
# containing reference/technical data |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# [M1] "[MEncoder-users] Thumbnail creation" |
# <http://lists.mplayerhq.hu/pipermail/mencoder-users/2006-August/003843.html> |
# [VC1] VC-1 and derived codecs information |
# <http://wiki.multimedia.cx/index.php?title=VC-1> |
# |
declare -r VERSION="1.0.12" |
# {{{ # CHANGELOG |
# History (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.12: (2008-04-16) |
# * BUGFIX/COSMETIC: Corrected 0ms timestamps |
# * COSMETIC: Re-added the (disabled for long) black border after highlights |
# * BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
# * FEATURE: Added a minimun length to use the end offset |
# * BUGFIX: Fixed the regression on highlights from the last version (extra |
# padding was being added my IM automatically) |
# * INTERNAL: Simplified use of IM's identify |
# * BUGFIX: Fixed parsing of manual timestamps including milliseconds |
# (when seconds didn't include the s character they were accidentally |
# multiplied by 10!) |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# This is the horizontal padding added to each capture. Changing it might break |
# extended set's alignement so keep this in mind if you tinker with it |
declare -ri HPAD=8 |
# }}} # End of constants |
# {{{ # Override-able variables |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# If the video is less than this length, end offset won't be used at all |
declare MIN_LENGTH_FOR_END_OFFSET=19m30s |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Experimental in 1.0.7b: transformations/filters |
# Operations are decomposed into independent optional steps, this will allow |
# to add some intermediate steps (e.g. polaroid mode) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' ) |
# Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# See csheet_montage for more details |
declare -i DISABLE_SHADOWS=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'MIN_LENGTH_FOR_END_OFFSET' |
'DEBUG' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEFAULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == "$(bc -q <<<"$1 $op $3")" ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $FUNCNAME $@ |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/([0-9])\./\1 + ./g' <<<"$t") # seconds followed by ms, with no "S" |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/\.([0-9]+)/0.\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s ms |
if grep -q '\.' <<<"$t" ; then |
# Have ms |
s=$(cut -d'.' -f1 <<<$t) |
ms=$(cut -d'.' -f2 <<<$t) |
else |
s=$t |
ms=0 |
fi |
local R="" |
if [ $h -gt 0 ]; then |
R="$h:" |
# Unreproducible bug reported by wdef: Minutes printed as hours |
# fixed with "else R="00:"" |
fi |
R="$R$(pad 2 "$m"):$(pad 2 $s)" |
# Milliseconds, only supported by ffmpeg, not printed otherwise |
if [ $decoder -eq $DEC_FFMPEG ]; then |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R="$R.$ms" |
fi |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# At least the busybox implementation is a real world du in usage that doesn't allow |
# using --bytes. Note that using "ls -H" is not an option either for the same reason. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "$runlen" -lt $(get_interval "$MIN_LENGTH_FOR_END_OFFSET") ; then |
# Min length to use end offset not met, it won't be used |
inf "End offset won't be used, video too short." |
eo=0 |
elif fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
# TODO: Is there a standard for PAL yet? |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
{ |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss $stamp $shoehorned "$f" |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" ) " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 |
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten " |
} |
# Apply a Polaroid-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad=$HPAD vpad=4 |
# Using transparent seems to make -shadow futile |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# This should actually be moved to a filter but with the current |
# architecture I'm unable to come up with the correct convert options |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
# This produces soft-shadows, which look much better than the montage ones |
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
fi |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas |
inf "Creating polaroid base canvas $row/$numrows..." |
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile" |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile" |
cmdopts="$cmdopts -geometry +${accoffset}+0 '$1' -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing polaroid row $row/$numrows..." |
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging polaroid rows..." |
output=$(new_temp_file .png) |
# Standard composition |
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png |
# Overlapped composition |
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output" |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the top corners are too near the heading, we add some space |
# with -splce |
eval convert -background transparent "$output" $cmdopts -trim +repage \ |
-bordercolor Transparent -splice 0x10 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [ $manual_mode -eq 1 ]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( ${initial_stamps[@]} ) |
else |
TIMECODES=${initial_stamps[@]} |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File 0000000$f.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
local geometry=$(identify -format '%wx%h' "$hlfile") |
convert \( -size "$geometry" xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size "$(cut -d'x' -f1<<<$geometry)"x1 xc:black \) \ |
"$output" -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"Filename:" \ |
-font "$fn_font" label:"$(basename "$f")" +append \ |
\) \ |
-font "$font_heading" \ |
label:"File size: $(get_pretty_size "$f")" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30s 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, photoframe and overlap. |
Same as -kr -ko -kf. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E:d: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case $(tolower "$2") in |
p|polaroid) # Same as overlap + rotate + photoframe |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (no final s) is undocumented but will stay |
t|timestamps|timestamp) |
inf "Timestamps disabled." |
# TODO: Can array splicing be done in a saner way? |
declare -a tmp=${FILTERS_IND[@]} |
unset FILTERS_IND |
FILTERS_IND=${tmp[@]/filt_apply_stamp/} |
unset tmp |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
DISABLE_SHADOWS=1 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
DEBUG=1 |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# }}} # Command line parsing |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.12/Makefile |
---|
0,0 → 1,41 |
#!/usr/bin/make -f |
# $Id$ |
VER=$(shell grep VERSION vcs|head -n1|sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
check-no-svn: |
if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
prep: |
cp vcs CHANGELOG debian-package/ |
chmod -x vcs |
dist: check-no-svn prep gz bz2 plaintext changelog deb cleanup |
gz: |
cp vcs vcs-$(VER) |
gzip -9 vcs-$(VER) |
bz2: |
cp vcs vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
plaintext: |
mv vcs vcs-$(VER) |
changelog: |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
cleanup: |
$(RM) -i Makefile *.changes |
$(RM) -r debian-package |
deb: |
cd debian-package/ && dpkg-buildpackage -rfakeroot -us -uc -b |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.12 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
/ATTIC/video-contact-sheet/tags/1.0.11/vcs |
---|
0,0 → 1,2273 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008 Toni Corvera |
# with patches from Phil Grundig and suggestions/corrections from |
# many others (see homepage) |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# References: |
# Pages I've taken snippets from or wrote code based on them. |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# [M1] "[MEncoder-users] Thumbnail creation" |
# <http://lists.mplayerhq.hu/pipermail/mencoder-users/2006-August/003843.html> |
# [VC1] VC-1 and derived codecs information |
# <http://wiki.multimedia.cx/index.php?title=VC-1> |
# |
declare -r VERSION="1.0.11" |
# {{{ # CHANGELOG |
# History (The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.11: (2008-04-08) |
# * BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
# fail when setting the default timecode derivation to number of |
# captures instead of interval (i.e. when including timecode_from=8 in |
# the config file) (thanks to Chris Hills for the bug report) |
# * WORKAROUND: Fix for all-equal captures (seems to be a known problem |
# with mplayer [M1]) (contributed by Phil Grundig) |
# * RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
# arrays (contributed by Phil Grundig) |
# * COMPATIBILITY: Support alternative du syntax for compatibility with |
# busybox (based on Phil Grundig's contribution) |
# * COSMETIC: Don't print milliseconds when using mplayer as capturer |
# (they're not really meaningful then) (suggested by Phil Grundig) |
# * COSMETIC: Align the extended set captures (-e) and the standard set |
# (bug pointed by Chris Hills). Seems to fail at some (smaller?) |
# sizes. |
# "Funky" modes aren't correctly aligned yet. |
# * DEBUGGING: Added optional function call trace (by setting variable DEBUG |
# to 1) |
# * Added FOURCC for VC-1 |
# * COSMETIC: Fixed captures recount with multiple files (prompted by a |
# bugreport from Dougn Redhammer) |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# This is the horizontal padding added to each capture. Changing it might break |
# extended set's alignement so keep this in mind if you tinker with it |
declare -ri HPAD=8 |
# }}} # End of constants |
# {{{ # Override-able variables |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporal files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Experimental in 1.0.7b: transformations/filters |
# Operations are decomposed into independent optional steps, this will allow |
# to add some intermediate steps (e.g. polaroid mode) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' ) |
# Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# See csheet_montage for more details |
declare -i DISABLE_SHADOWS=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
'DEBUG' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEFAULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == $(bc -q <<<"$1 $op $3") ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/(\.[0-9]+)/0\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f1 |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f2 |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s=$(cut -d'.' -f1 <<<$t) |
local ms=$(cut -d'.' -f2 <<<$t) |
local R="" |
if [ $h -gt 0 ]; then |
R="$h:" |
# Unreproducible bug reported by wdef: Minutes printed as hours |
# fixed with "else R="00:"" |
fi |
R="$R$(pad 2 "$m"):$(pad 2 $s)" |
# Milliseconds, only supported by ffmpeg, not printed otherwise |
if [ $decoder -eq $DEC_FFMPEG ]; then |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R="$R.$ms" |
fi |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# At least the busybox implementation is a real world du in usage that doesn't allow |
# using --bytes. Note that using "ls -H" is not an option either for the same reason. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# |
# trace($1 = function name = $FUNCNAME, function arguments...) |
trace() { |
if [ "$DEBUG" -ne "1" ]; then return; fi |
echo "[TRACE]: $@" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $FUNCNAME $@ |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $FUNCNAME $@ |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $FUNCNAME $@ |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $FUNCNAME $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local stamp=$st |
local -a LTC |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $FUNCNAME $@ |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
# TODO: Is there a standard for PAL yet? |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
trace $FUNCNAME $@ |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000005.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
{ |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss $stamp $shoehorned "$f" |
} >"$stdout" 2>"$stderr" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filter_vidcap() { |
trace $FUNCNAME $@ |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts="$cmdopts $( $filter "$1" "$2" "$3" "$4" ) " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $FUNCNAME $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_apply_stamp() { |
trace $FUNCNAME $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 |
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten " |
} |
# Apply a Polaroid-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $FUNCNAME $@ |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $FUNCNAME $@ |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $FUNCNAME $@ |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $FUNCNAME $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad=$HPAD vpad=4 |
# Using transparent seems to make -shadow futile |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# This should actually be moved to a filter but with the current |
# architecture I'm unable to come up with the correct convert options |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
# This produces soft-shadows, which look much better than the montage ones |
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
fi |
#convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $FUNCNAME $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas |
inf "Creating polaroid base canvas $row/$numrows..." |
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile" |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile" |
cmdopts="$cmdopts -geometry +${accoffset}+0 '$1' -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing polaroid row $row/$numrows..." |
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging polaroid rows..." |
output=$(new_temp_file .png) |
# Standard composition |
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png |
# Overlapped composition |
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output" |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the top corners are too near the heading, we add some space |
# with -splce |
eval convert -background transparent "$output" $cmdopts -trim +repage \ |
-bordercolor Transparent -splice 0x10 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $FUNCNAME $@ |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
trace $FUNCNAME $@ |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $FUNCNAME $@ |
local f=$1 |
local numcols= |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000005.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "File 0000000$f.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
# mplayer will re-write also 00000001.png-00000004.png |
if [ $decoder -eq $DEC_MPLAYER ]; then |
for f_ in 1 2 3 4; do |
if [ -f "0000000${f_}.png" ]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
let 'w=vidcap_width/2-HPAD, h=vidcap_height*w/vidcap_width' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(identify "$output" | cut -d' ' -f3 | cut -d'x' -f1) |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"Filename:" \ |
-font "$fn_font" label:"$(basename "$f")" +append \ |
\) \ |
-font "$font_heading" \ |
label:"File size: $(get_pretty_size "$f")" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, photoframe and overlap. |
Same as -kr -ko -kf. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E:d: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case $(tolower "$2") in |
p|polaroid) # Same as overlap + rotate + photoframe |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (no final s) is undocumented but will stay |
t|timestamps|timestamp) |
inf "Timestamps disabled." |
# TODO: Can array splicing be done in a saner way? |
declare -a tmp=${FILTERS_IND[@]} |
unset FILTERS_IND |
FILTERS_IND=${tmp[@]/filt_apply_stamp/} |
unset tmp |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
DISABLE_SHADOWS=1 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# }}} # Command line parsing |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/debian/changelog |
---|
0,0 → 1,5 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Package: vcs |
Architecture: all |
Depends: bc, bash, grep, imagemagick (>= 6.0), mktemp, mplayer, ffmpeg |
Description: vcs is a script that creates a contact sheet (preview) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
. |
Upstream homepage <http://p.outlyer.net/vcs/>. |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/debian/dirs |
---|
0,0 → 1,0 |
usr/bin |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/debian/copyright |
---|
0,0 → 1,37 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
# Please also look if there are files or directories which have a |
# different copyright/license attached and list them here. |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(CURDIR)/debian/vcs install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.11/debian-package/Makefile |
---|
0,0 → 1,14 |
# $Id$ |
prefix:=/usr |
DESTDIR:=/ |
all: |
clean: |
install: |
mkdir -p $(DESTDIR)$(prefix)/bin |
install -m755 -o0 -g0 vcs $(DESTDIR)$(prefix)/bin |
.PHONY: all install clean |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.11/Makefile |
---|
0,0 → 1,41 |
#!/usr/bin/make -f |
# $Id$ |
VER=$(shell grep VERSION vcs|head -n1|sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
check-no-svn: |
if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
prep: |
cp vcs CHANGELOG debian-package/ |
chmod -x vcs |
dist: check-no-svn prep gz bz2 plaintext changelog deb cleanup |
gz: |
cp vcs vcs-$(VER) |
gzip -9 vcs-$(VER) |
bz2: |
cp vcs vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
plaintext: |
mv vcs vcs-$(VER) |
changelog: |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
cleanup: |
$(RM) -i Makefile *.changes |
$(RM) -r debian-package |
deb: |
cd debian-package/ && dpkg-buildpackage -rfakeroot -us -uc -b |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.11/CHANGELOG |
---|
0,0 → 1,177 |
1.0.11: (2008-04-08) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
1.0.9a: (2007-06-10) (-Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.11 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.10/vcs |
---|
0,0 → 1,2186 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# References: |
# Pages I've taken snippets from or wrote code based on them. |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# |
declare -r VERSION="1.0.10" |
# {{{ # CHANGELOG |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.10: (2007-11-08) |
# * BUGFIX: Corrected aspect guessing bug: would fail if width |
# was standard but height not |
# * FEATURE: Allow explicitly disabling timestamps (-dt or |
# --disable timestamps) |
# * FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
# * Added HD resolution guessed aspect ratio (defaults to 16/9) |
# * OTHER: Changed e-mail address in the comments to gmail's, would probably |
# get a quicker response. |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF=( ) # Temporal files |
declare -a TIMECODES=( ) # Timestamps of the video captures |
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Experimental in 1.0.7b: transformations/filters |
# Operations are decomposed into independent optional steps, this will allow |
# to add some intermediate steps (e.g. polaroid mode) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' ) |
# Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS=( ) |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# See chseet_montage for more details |
declare -i DISABLE_SHADOWS=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEAFULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == $(bc -q <<<"$1 $op $3") ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/(\.[0-9]+)/0\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f1 |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f2 |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s=$(cut -d'.' -f1 <<<$t) |
local ms=$(cut -d'.' -f2 <<<$t) |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s).$ms |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp=$st |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [ -z "$ar" ]; then |
if [ $h -eq 720 ] || [ $h -eq 1080 ]; then # HD |
# TODO: Is there a standard for PAL yet? |
ar=16/9 |
fi |
fi |
if [ -z "$ar" ]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000001.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
{ |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" |
} >"$stdout" 2>"$stderr" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filter_vidcap() { |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts+=" $( $filter "$1" "$2" "$3" "$4" ) " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_apply_stamp() { |
local filename=$1 timestamp=$2 width=$3 height=$4 |
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten " |
} |
# Apply a Polaroid-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
case $ctx in |
$CTX_STD|$CTX_HL) hpad=10 vpad=5 ;; |
$CTX_EXT) hpad=5 vpad=2 ;; |
*) error "Internal error" && return $EX_SOFTWARE ;; |
esac |
# Using transparent seems to make -shadow futile |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# This should actually be moved to a filter but with the current |
# architecture I'm unable to come up with the correct convert options |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
# This produces soft-shadows, which look much better than the montage ones |
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
fi |
#convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles=( ) |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas |
inf "Creating polaroid base canvas $row/$numrows..." |
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile" |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile" |
cmdopts+=" -geometry +${accoffset}+0 '$1' -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing polaroid row $row/$numrows..." |
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging polaroid rows..." |
output=$(new_temp_file .png) |
# Standard composition |
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png |
# Overlapped composition |
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output" |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts+=" -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the top corners are too near the heading, we add some space |
# with -splce |
eval convert -background transparent "$output" $cmdopts -trim +repage \ |
-bordercolor Transparent -splice 0x10 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
local f=$1 |
local numcols= |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= capfiles=( ) |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 capfiles=( ) |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps aren't included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= capfiles=( ) |
# The image size of the extra captures is 1/4 |
let 'w=vidcap_width/2, h=vidcap_height/2' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(identify "$output" | cut -d' ' -f3 | cut -d'x' -f1) |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"Filename:" \ |
-font "$fn_font" label:"$(basename "$f")" +append \ |
\) \ |
-font "$font_heading" \ |
label:"File size: $(get_pretty_size "$f")" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or |
fractions. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, photoframe and overlap. |
Same as -kr -ko -kf. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E:d: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,disable:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case $(tolower "$2") in |
p|polaroid) # Same as overlap + rotate + photoframe |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (no final s) is undocumented but will stay |
t|timestamps|timestamp) |
inf "Timestamps disabled." |
# TODO: Can array splicing be done in a saner way? |
declare -a tmp=${FILTERS_IND[@]} |
unset FILTERS_IND |
FILTERS_IND=${tmp[@]/filt_apply_stamp/} |
unset tmp |
;; |
s|shadows|shadow) |
if [ $DISABLE_SHADOWS -eq 0 ]; then |
inf "Shadows disabled." |
DISABLE_SHADOWS=1 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# }}} # Command line parsing |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.10/CHANGELOG |
---|
0,0 → 1,154 |
1.0.10: (2007-11-08) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
1.0.9a: (2007-06-10) (-Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.10/Makefile |
---|
0,0 → 1,19 |
#!/usr/bin/make -f |
VER=$(shell grep VERSION vcs|head -n1|sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
dist: |
if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
cp vcs vcs-$(VER) |
gzip -9 vcs-$(VER) |
cp vcs vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
mv vcs vcs-$(VER) |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
rm -i Makefile |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.10 |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.9a/CHANGELOG |
---|
0,0 → 1,144 |
1.0.9a: (2007-06-10) (-Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.9a/vcs |
---|
0,0 → 1,2126 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
# References: |
# Pages from I've taken snippets or wrote code based on them. |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# |
declare -r VERSION="1.0.9a" |
# {{{ # CHANGELOG |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.9a: (2007-06-10) |
# * BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
# broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
# * BUGFIX: Use the computed number of columns for extended mode |
# (instead of the global one) |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF=( ) # Temporal files |
declare -a TIMECODES=( ) # Timestamps of the video captures |
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Experimental in 1.0.7b: transformations/filters |
# Operations are decomposed into independent optional steps, this will allow |
# to add some intermediate steps (e.g. polaroid mode) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' ) |
# Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS=( ) |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEAFULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == $(bc -q <<<"$1 $op $3") ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/(\.[0-9]+)/0\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f1 |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f2 |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s=$(cut -d'.' -f1 <<<$t) |
local ms=$(cut -d'.' -f2 <<<$t) |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s).$ms |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp=$st |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
if [ $w -eq 352 ]; then # VCD / DVD @ VCD Res. / Half-D1 / CVD |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then # Half-D1 / CVD |
ar=4/3 |
fi |
elif [ $w -eq 704 ] || [ $w -eq 720 ]; then # DVD / DVB |
# Actually for 720x576/720x480 16/9 is as good a guess |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
ar=4/3 |
fi |
elif [ $w -eq 480 ]; then # SVCD |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
ar=4/3 |
fi |
else |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000001.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
{ |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" |
} >"$stdout" 2>"$stderr" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filter_vidcap() { |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts+=" $( $filter "$1" "$2" "$3" "$4" ) " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_apply_stamp() { |
local filename=$1 timestamp=$2 width=$3 height=$4 |
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten " |
} |
# Apply a Polaroid-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
case $ctx in |
$CTX_STD|$CTX_HL) hpad=10 vpad=5 ;; |
$CTX_EXT) hpad=5 vpad=2 ;; |
*) error "Internal error" && return $EX_SOFTWARE ;; |
esac |
# Using transparent seems to make -shadow futile |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# This produces soft-shadows, which look much better than the montage ones |
# |
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles=( ) |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas |
inf "Creating polaroid base canvas $row/$numrows..." |
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile" |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile" |
cmdopts+=" -geometry +${accoffset}+0 '$1' -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing polaroid row $row/$numrows..." |
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging polaroid rows..." |
output=$(new_temp_file .png) |
# Standard composition |
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png |
# Overlapped composition |
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output" |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts+=" -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the top corners are too near the heading, we add some space |
# with -splce |
eval convert -background transparent "$output" $cmdopts -trim +repage \ |
-bordercolor Transparent -splice 0x10 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
local f=$1 |
local numcols= |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= capfiles=( ) |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 capfiles=( ) |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps aren't included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= capfiles=( ) |
# The image size of the extra captures is 1/4 |
let 'w=vidcap_width/2, h=vidcap_height/2' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(identify "$output" | cut -d' ' -f3 | cut -d'x' -f1) |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"Filename:" \ |
-font "$fn_font" label:"$(basename "$f")" +append \ |
\) \ |
-font "$font_heading" \ |
label:"File size: $(get_pretty_size "$f")" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or |
fractions. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, photoframe and overlap. |
Same as -kr -ko -kf. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,end_offset:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case $(tolower "$2") in |
p|polaroid) # Same as overlap + rotate + photoframe |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# }}} # Command line parsing |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.9a/Makefile |
---|
0,0 → 1,19 |
#!/usr/bin/make -f |
VER=$(shell grep VERSION vcs|head -n1|sed -r 's/.*"(.*)".*/\1/g') |
all: |
@echo "Use $(MAKE) dist" |
dist: |
if [ -d .svn ]; then echo "Don't release from SVN working copy" ; false ; fi |
cp vcs vcs-$(VER) |
gzip -9 vcs-$(VER) |
cp vcs vcs-$(VER) |
bzip2 -9 vcs-$(VER) |
mv vcs vcs-$(VER) |
gzip -9 CHANGELOG |
gzip -dc CHANGELOG.gz > CHANGELOG |
rm -i Makefile |
.PHONY: dist |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.9a |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.8a/CHANGELOG |
---|
0,0 → 1,138 |
1.0.8a: (2007-06-02) (Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.8a/vcs |
---|
0,0 → 1,2126 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
# References: |
# Pages from I've taken snippets or wrote code based on them. |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# |
declare -r VERSION="1.0.8a" |
# {{{ # CHANGELOG |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.8a: (2007-06-02) |
# * BUGFIX: User set number of columns wasn't being used if -n wasn't used |
# (Thanks to 'Homer S') |
# * BUGFIX: Right side of heading wasn't using the user's font colour |
# (Thanks to 'Dougn Redhammer'). |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF=( ) # Temporal files |
declare -a TIMECODES=( ) # Timestamps of the video captures |
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Experimental in 1.0.7b: transformations/filters |
# Operations are decomposed into independent optional steps, this will allow |
# to add some intermediate steps (e.g. polaroid mode) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' ) |
# Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS=( ) |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEAFULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == $(bc -q <<<"$1 $op $3") ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/(\.[0-9]+)/0\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f1 |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f2 |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s=$(cut -d'.' -f1 <<<$t) |
local ms=$(cut -d'.' -f2 <<<$t) |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s).$ms |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp=$st |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
if [ $w -eq 352 ]; then # VCD / DVD @ VCD Res. / Half-D1 / CVD |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then # Half-D1 / CVD |
ar=4/3 |
fi |
elif [ $w -eq 704 ] || [ $w -eq 720 ]; then # DVD / DVB |
# Actually for 720x576/720x480 16/9 is as good a guess |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
ar=4/3 |
fi |
elif [ $w -eq 480 ]; then # SVCD |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
ar=4/3 |
fi |
else |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000001.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
{ |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" |
} >"$stdout" 2>"$stderr" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filter_vidcap() { |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts+=" $( $filter "$1" "$2" "$3" "$4" ) " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_apply_stamp() { |
local filename=$1 timestamp=$2 width=$3 height=$4 |
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten " |
} |
# Apply a Polaroid-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
case $ctx in |
$CTX_STD|$CTX_HL) hpad=10 vpad=5 ;; |
$CTX_EXT) hpad=5 vpad=2 ;; |
*) error "Internal error" && return $EX_SOFTWARE ;; |
esac |
# Using transparent seems to make -shadow futile |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# This produces soft-shadows, which look much better than the montage ones |
# |
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles=( ) |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas |
inf "Creating polaroid base canvas $row/$numrows..." |
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile" |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile" |
cmdopts+=" -geometry +${accoffset}+0 '$1' -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing polaroid row $row/$numrows..." |
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging polaroid rows..." |
output=$(new_temp_file .png) |
# Standard composition |
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png |
# Overlapped composition |
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output" |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts+=" -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the top corners are too near the heading, we add some space |
# with -splce |
eval convert -background transparent "$output" $cmdopts -trim +repage \ |
-bordercolor Transparent -splice 0x10 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
local f=$1 |
local numcols= |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
create_temp_dir |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= capfiles=( ) |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 capfiles=( ) |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
let 'n--' # there's an extra inc |
if [ "$n" -lt "$cols" ]; then |
numcols=$n |
else |
numcols=$cols |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n numcols |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps aren't included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= capfiles=( ) |
# The image size of the extra captures is 1/4 |
let 'w=vidcap_width/2, h=vidcap_height/2' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
let 'n--' # There's an extra inc |
if [ $n -lt $(( $cols * 2 )) ]; then |
numcols=$n |
else |
numcols=$(( $cols * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $cols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(identify "$output" | cut -d' ' -f3 | cut -d'x' -f1) |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"Filename:" \ |
-font "$fn_font" label:"$(basename "$f")" +append \ |
\) \ |
-font "$font_heading" \ |
label:"File size: $(get_pretty_size "$f")" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$fg_heading" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or |
fractions. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, photoframe and overlap. |
Same as -kr -ko -kf. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,end_offset:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case $(tolower "$2") in |
p|polaroid) # Same as overlap + rotate + photoframe |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# }}} # Command line parsing |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.8a |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.7a/CHANGELOG |
---|
0,0 → 1,132 |
1.0.7a: (2007-05-12) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho font, |
it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.7a/vcs |
---|
0,0 → 1,2144 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
# References: |
# Pages from I've taken snippets or wrote code based on them. |
# I refer to them in comments as e.g. [[R1]]. [[R1#3]] Means section 3 in R1. |
# I also use internal references in the form [x1] (anchor -where to point-) |
# and [[x1]] (x-reference -point to what-). |
# [R0] getopt-parse.bash example, on Debian systems: |
# /usr/share/doc/util-linux/examples/getopt-parse.bash.gz |
# [R1] Bash (and other shells) tips |
# <http://wooledge.org/mywiki/BashFaq> |
# [R2] List of officially registered FOURCCs and WAVE Formats (aka wFormatTag) |
# <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# [R3] Unofficial list of FOURCCs |
# <http://www.fourcc.org/> |
# [R4] A php module with a list of FOURCCs and wFormatTag's mappings |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php> |
# |
declare -r VERSION="1.0.7a" |
# {{{ # CHANGELOG |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.7a: (2007-05-12) |
# * Print title *before* the highlights. |
# * Added the forgotten -O and -c to the help text (oops!) |
# * Experimental: Allow using non-latin alphabets by switching font. See -I. |
# It only affects the filename! Also allow overriding the font to be used |
# to print the filename ($font_filename). Right now only using a Mincho font, |
# it can be overriding by overriding $FONT_MINCHO. |
# * Make title font size independent of the timestamps size. And allow |
# overriding the title font ($font_title), font size ($pts_title) |
# and colours ($fg_title and $bg_title). |
# * Allow overriding the previews' background ($bg_contact) |
# * Added getopt, identify, sed, grep and egrep to the checked programs |
# * BUGFIX: Corrected test of accepted characters for intervals |
# * INTERNAL: New parsing code |
# * FEATURE: Replaced hard by soft shadows |
# * BUGFIX: Corrected console colour usage: Print the colours to the correct |
# channel |
# * Made tput (coloured console output) optional (AFAIK should be present in |
# any sane system though). |
# * FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
# version), Photoframe and Random colours/fonts. (see --help) |
# * INTERNAL: Use /dev/shm as base tempdir if possible |
# * BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
# * Added workaround for ffmpeg arguments order |
# * Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
# * INTERNAL: Renamed info() to inf() to eliminate ambiguities |
# * INTERNAL: guess_aspect() doesn't operate globally |
# * Reorganized help by alphabetical/rarity order |
# * FEATURE: Full milliseconds support (actually, full decimal point seconds), |
# timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
# * BUGFIX/FEATURE: The number of extended captures is rounded to match the |
# standard columns (extended width matches standard) |
# * Made FOURCCs list case sensitive (the list has grown enough that I no |
# longer see a benefit in being ambigous) |
# * Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
# Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
# codecs. |
# * Added -E / --end_offset / $end_offset, used to eliminate some |
# seconds from the end |
# * FEATURE: Anonymous mode (use --anonymous or -U0) |
# }}} # CHANGELOG |
set -e |
# {{{ # TODO |
# TODO / FIXME: |
# * [[R1#22]] states that not all bc versions understand '<', more info required |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * [[x2]] Find out if egrep is safe to use or grep -E is more commonplace. |
# |
# }}} # TODO |
# {{{ # Constants |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# see $font_filename and $FONT_MINCHO |
declare -ri FF_DEFAULT=5 FF_MINCHO=7 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# }}} # End of constants |
# {{{ # Override-able variables |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare bg_title=White # Background for the title (see -T) |
declare bg_contact=White # Background of the thumbnails |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
declare fg_title=Black # Font colour fot the title |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps over the thumbnails |
declare font_heading=helvetica # Used for the heading (meta info box) |
declare font_sign=$font_heading # Used for the signature box |
# Unlike other font_ variables this doesn't take a font name directly |
# but is restricted to the $FF_ values. This is to allow overrides |
# from the command line to be placed anywhere, i.e. in |
# $ vcs -I file.avi -O 'FONT_MINCHO=whatever' |
# as the font is overridden is after requesting its use, it wouldn't be |
# affected |
# The other font_ variables are only affected by overrides and not command |
# line options that's why this one is special. |
declare font_filename=$FF_DEFAULT # Used to print only the filename in the heading |
declare font_title=$font_heading # Used fot the title (see -T) |
# Font sizes, in points |
declare -i pts_tstamps=18 # Used for the timestamps |
declare -i pts_meta=16 # Used for the meta info box |
declare -i pts_sign=11 # Used for the signature |
declare -i pts_title=36 # Used for the title (see -T) |
# See --shoehorn |
declare shoehorned= |
# See -E / $end_offset |
declare -i DEFAULT_END_OFFSET=60 |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
#declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# Experimental in 1.0.7b: |
# Experiment to get international font support |
# I'll need to get some help here, so if you use anything beyond a latin |
# alphabet, please help me choosing the correct fonts |
# To my understanding Ming/Minchō fonts should cover most of Japanse, |
# Chinese and Korean |
# Apparently Kochi Mincho should include Hangul *and* Cyrillic... which would be |
# great :) Although it couldn't write my hangul test, and also the default font |
# (helvetica) in my system seems to include cyrillic too, or at least a subset of |
# it. |
declare FONT_MINCHO=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
# Output of capturing programs is redirected here |
declare stdout=/dev/null stderr=/dev/null |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare fromtime=0 # Starting second (see -f) |
declare totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF=( ) # Temporal files |
declare -a TIMECODES=( ) # Timestamps of the video captures |
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# This number of seconds is *not* captured from the end of the video |
declare -i end_offset=$DEFAULT_END_OFFSET |
# Experimental in 1.0.7b: transformations/filters |
# Operations are decomposed into independent optional steps, this will allow |
# to add some intermediate steps (e.g. polaroid mode) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height ) |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' ) |
# Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS=( ) |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Gravity of the timestamp (will be override-able in the future) |
declare grav_timestamp=SouthEast |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i anonymous_mode=0 |
# }}} # Variables |
# {{{ # Configuration handling |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
# 'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
'FONT_MINCHO' |
'stdout' |
'stderr' |
'DEFAULT_END_OFFSET' |
) |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -D |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
# Override-able hack, this won't work with command line overrides, though |
end_offset=$DEFAULT_END_OFFSET |
interval=$DEFAULT_INTERVAL |
numcaps=$DEAFULT_NUMCAPS |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction (*strictly*, i.e. "1" is not a fraction) |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { |
tr '[A-Z]' '[a-z]' <<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
#local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
#bc -q <<<"( $f + 0.5 ) / 1" |
bc -q <<<"scale=5; v=( ($exp) + 0.5 ) ; scale=0 ; v/1" |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
bc -q <<<"( $f + 0.999999999 ) / 1" |
} |
# Round to a multiple |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [ $r -ne 0 ]; then |
let 'n += ( d - r )' |
fi |
echo $n |
} |
# numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
fptest() { |
local op= |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
*) error "Internal error" && return $EX_SOFTWARE |
esac |
[ '1' == $(bc -q <<<"$1 $op $3") ] |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
bc -ql <<<"sqrt( $1^2 + $2^2)" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tolower "$1") t r |
# Only allowed characters |
if ! egrep -qi '^[0-9smh.]+$' <<<"$s"; then |
return $EX_USAGE; |
fi |
# New parsing code: replaces units by a product |
# and feeds the resulting string to bc |
t=$s |
t=$(sed -r 's/([0-9]+)h/ ( \1 * 3600 ) + /g' <<<$t) |
t=$(sed -r 's/([0-9]+)m/ ( \1 * 60 ) + /g' <<<$t) |
t=$(sed 's/s/ + /g' <<<$t) |
t=$(sed -r 's/\.\.+/./g'<<<$t) |
t=$(sed -r 's/(\.[0-9]+)/0\1 + /g' <<<$t) |
t=$(sed -r 's/\+ ?$//g' <<<$t) |
r=$(bc -lq <<<$t 2>/dev/null) # bc parsing fails with correct return code |
if [ -z "$r" ]; then |
return $EX_USAGE |
fi |
# Negative interval |
if [ "-" == ${r:0:1} ]; then |
return $EX_USAGE |
fi |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# printf "%0${1}d\n" "$2" # [[R1#18]] # Can't be used with non-numbers |
local str=$2 |
while [ "${#str}" -lt $1 ]; do |
str="0$str" |
done |
echo $str |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f1 |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify "$1" | cut -d' ' -f3 | cut -d'x' -f2 |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_float "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
#local h=$(( $t / 3600 )) |
# bc's modulus seems to *require* not using the math lib (-l) |
local h=$( bc -q <<<"$t / 3600") |
t=$(bc -q <<<"$t % 3600") |
local m=$( bc -q <<<"$t / 60") |
t=$(bc -q <<<"$t % 60") |
local s=$(cut -d'.' -f1 <<<$t) |
local ms=$(cut -d'.' -f2 <<<$t) |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
# Right pad of decimal seconds |
if [ ${#ms} -lt 2 ]; then |
ms="${ms}0" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s).$ms |
# Trim (most) decimals |
sed -r 's/\.([0-9][0-9]).*/.\1/'<<<$R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input extension |
local iext=$(sed -r 's/.*\.(.*)/\1/' <<<$to) |
# Input filename without extension |
local b=${to%.$iext} |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s#%b#$b#g" <<<"$safe_rename_pattern") |
to=$(sed "s#%N#$n#g" <<<"$to") |
to=$(sed "s#%e#$ext#g" <<<"$to") |
let 'n++'; |
done |
mv -- "$from" "$to" |
echo "$to" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in getopt mplayer convert montage identify bc \ |
ffmpeg mktemp sed grep egrep cut; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
# TODO: [x2] |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" ; tput sgr0 |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be coloured |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" ; tput sgr0 |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
if [ -d /dev/shm ] && [ -w /dev/shm ]; then |
VCSTEMPDIR=$(mktemp -d -p /dev/shm vcs.XXXXXX) |
else |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
fi |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Randomizes the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
local mode=f lineno |
if [ "f" == $mode ]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $RANDOM % $ncolours ) )) |
convert -list color | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $RANDOM + $RANDOM + ($RANDOM % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
local nfonts=$(( $(convert -list type | wc -l) - 5 )) |
randfont() { |
lineno=$(( 5 + ( $RANDOM % $nfonts ))) |
convert -list type | sed -n "${lineno}p" | cut -d' ' -f1 # [[R1#19]] |
} |
bg_heading=$(randcolour) |
bg_sign=$(randcolour) |
bg_title=$(randcolour) |
bg_contact=$(randcolour) |
fg_heading=$(randcolour) |
fg_sign=$(randcolour) |
fg_tstamps=$(randcolour) |
fg_title=$(randcolour) |
font_tstamps=$(randfont) |
font_heading=$(randfont) |
font_sign=$(randfont) |
font_title=$(randfont) |
inf "Randomization result: |
Chosen backgrounds: |
'$bg_heading' for the heading |
'$bg_sign' for the signature |
'$bg_title' for the title |
'$bg_contact' for the contact sheet |
Chosen font colours: |
'$fg_heading' for the heading |
'$fg_sign' for the signature |
'$fg_title' for the title |
'$fg_tstamps' for the timestamps, |
Chosen fonts: |
'$font_heading' for the heading |
'$font_sign' for the signature |
'$font_title' for the title |
'$font_tstamps' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
# globals: fromtime, totime, timecode_from, TIMECODES, end_offset |
if fptest $st -lt $fromtime ; then |
st=$fromtime |
fi |
if fptest $totime -gt 0 && fptest $end -gt $totime ; then |
end=$totime |
fi |
if fptest $totime -le 0 ; then # If no totime is set, use end_offset |
eo=$end_offset |
local runlen=$( bc <<<"$end - $st" ) |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && fptest "$eo" -eq "$DEFAULT_END_OFFSET" ; then |
warn "Default end offset was too high, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$( bc -lq <<< "scale=3; ($end-$st)/2 + 1" ) |
else |
#inc=$(( ($end-$st) / $tcnumcaps )) |
# FIXME: The last second is avoided (-1) to get the correct caps number |
inc=$( bc -lq <<< "scale=3; ($end-$eo-$st)/$tcnumcaps" ) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp=$st |
while fptest $stamp -le $(bc -q <<<"$end-$eo"); do |
if fptest $stamp -lt 0 ; then |
error "Internal error, negative timestamp calculated!" |
return $EX_SOFTWARE |
fi |
LTC+=( $stamp ) |
stamp=$(bc -q <<<"$stamp+$inc") |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[@]} ${LTC[@]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=$1 h=$2 ar |
if [ $w -eq 352 ]; then # VCD / DVD @ VCD Res. / Half-D1 / CVD |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
ar=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then # Half-D1 / CVD |
ar=4/3 |
fi |
elif [ $w -eq 704 ] || [ $w -eq 720 ]; then # DVD / DVB |
# Actually for 720x576/720x480 16/9 is as good a guess |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
ar=4/3 |
fi |
elif [ $w -eq 480 ]; then # SVCD |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
ar=4/3 |
fi |
else |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000001.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
{ |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" |
} >"$stdout" 2>"$stderr" |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: It would be nice to show a message if it takes too long |
{ |
# See wa_ss_* declarations at the start of the file for details |
ffmpeg -y ${wa_ss_be/ / $stamp} -i "$f" ${wa_ss_af/ / $stamp} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE |
# Used to test bogus files (e.g. to test codec ids) |
#convert -size 1x xc:black $VIDCAPFILE |
} >"$stdout" 2>"$stderr" |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filter_vidcap() { |
# For performance purposes each filter simply prints a set of options |
# to 'convert'. That's less flexible but enough right now for the current |
# filters. |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
cmdopts+=" $( $filter "$1" "$2" "$3" "$4" ) " |
done |
local t=$(new_temp_file .png) |
eval "convert '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[ -f "$t" ] && mv "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
echo " \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_apply_stamp() { |
local filename=$1 timestamp=$2 width=$3 height=$4 |
echo -n " \( -box '#000000aa' -fill '$fg_tstamps' -pointsize '$pts_tstamps' " |
echo -n " -gravity '$grav_timestamp' -stroke none -strokewidth 3 -annotate +5+5 " |
echo " ' $(pretty_stamp $stamp) ' \) -flatten " |
} |
# Apply a Polaroid-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
# local file="$1" ts=$2 w=$3 h=$4 |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# TODO: Split softshadow in a filter |
echo -n "-bordercolor white -border 6 -bordercolor grey60 -border 1 " |
echo -n "-background black \( +clone -shadow 60x4+4+4 \) +swap " |
echo "-background none -flatten -trim +repage" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
# Rotation angle [-18..18] |
local angle=$(( ($RANDOM % 37) - 18 )) |
echo "-background none -rotate $angle " |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
local file="$1" ts=$2 w=$3 h=$4 |
# Base reel dimensions |
local rw=$(rmultiply $w,0.08) # 8% width |
local rh=$(( $rw / 2 )) |
# Ellipse center |
local ecx=$(( $rw / 2 )) ecy=0 |
# Ellipse x, y radius |
local erx=$(( (rw/2)*60/100 )) # 60% halt rect width |
local ery=$(( $erx / 2)) |
local base_reel=$(new_temp_file .png) reel_strip=$(new_temp_file .png) |
# Create the reel pattern... |
convert -size ${rw}x${rh} 'xc:black' \ |
-fill white -draw "ellipse $ecx,$ecy $erx,$ery 0,360" -flatten \ |
\( +clone -flip \) -append \ |
-fuzz '40%' -transparent white \ |
"$base_reel" |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local sh=$(imh "$base_reel") in= |
local repeat=$( ceilmultiply $h/$sh) |
while [ $repeat -gt 1 ]; do |
in+=" '$base_reel' " |
let 'repeat--' |
done |
eval convert "$base_reel" $in -append -crop $(imw "$base_reel")x${h}+0+0 \ |
"$reel_strip" |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
echo -n "'$reel_strip' +append -flop '$reel_strip' +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
case $ctx in |
$CTX_STD|$CTX_HL) hpad=10 vpad=5 ;; |
$CTX_EXT) hpad=5 vpad=2 ;; |
*) error "Internal error" && return $EX_SOFTWARE ;; |
esac |
# Using transparent seems to make -shadow futile |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
# This produces soft-shadows, which look much better than the montage ones |
# |
convert \( -shadow 50x2+10+10 "$output" \) "$output" -composite "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles=( ) |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seq 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles+=( "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas |
inf "Creating polaroid base canvas $row/$numrows..." |
convert -size ${canvasw}x${canvash} xc:transparent "$rowfile" |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seq 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [ -z "$1" ]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
#convert -geometry +${accoffset}+0 "$rowfile" "$1" -composite "$rowfile" |
cmdopts+=" -geometry +${accoffset}+0 '$1' -composite " |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing polaroid row $row/$numrows..." |
eval convert "'$rowfile'" "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging polaroid rows..." |
output=$(new_temp_file .png) |
# Standard composition |
#convert -background Transparent "${rowfiles[@]}" -append polaroid.png |
# Overlapped composition |
convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent "$output" |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $RANDOM % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts+=" -geometry +$(( $RANDOM % $maxoffset ))+$accoffset '$row' -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the top corners are too near the heading, we add some space |
# with -splce |
eval convert -background transparent "$output" $cmdopts -trim +repage \ |
-bordercolor Transparent -splice 0x10 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_float "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
aspect_ratio=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio)" |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
create_temp_dir |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlighs |
local hlfile n=1 # hlfile Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlcapfile= pretty= capfiles=( ) |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt $numsecs ; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
capfiles+=( "$hlcapfile" ) |
let 'n++' |
done |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile pretty n=1 capfiles=( ) |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
# identified by capture number, padded to 6 characters |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' # $n++ |
done |
#filter_all_vidcaps "${capfiles[@]}" |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult "$(( ${#TIMECODES[@]} * $extended_factor ))" $((2*$numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps aren't included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= capfiles=( ) |
# The image size of the extra captures is 1/4 |
let 'w=vidcap_width/2, h=vidcap_height/2' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
filter_vidcap "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
capfiles+=( "$capfile" ) |
let 'n++' |
done |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $(($numcols * 2)) $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n |
fi # Extended mode |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
local vcodec= acodec= |
case "${VID[$VCODEC]}" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith Screen Capture Codec" ;; |
VP6[012]) vcodec="On2 Truemotion VP6" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported. |
XVID) vcodec="Xvid" ;; |
# These are known FourCCs that I haven't tested against so far |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
# Audio codec "prettyfication", see [[R4]] |
case $(tolower ${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
inf "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
# Add the background |
convert -background "$bg_contact" "$output" -flatten "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature |
if [ $anonymous_mode -eq 0 ]; then |
signature="$user_signature $user |
with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(identify "$output" | cut -d' ' -f3 | cut -d'x' -f1) |
# TODO: Use a better height calculation |
local headheight=$(($pts_meta * 4 )) |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [ "$title" ]; then |
# TODO: Use a better height calculation |
convert \ |
\( \ |
-size ${headwidth}x$(( $pts_title + ($pts_title/2) )) "xc:$bg_title" \ |
-font "$font_title" -pointsize "$pts_title" \ |
-background "$bg_title" -fill "$fg_title" \ |
-gravity Center -annotate 0 "$title" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
fi |
local fn_font= # see $font_filename |
case $font_filename in |
$FF_DEFAULT) fn_font="$font_heading" ;; |
$FF_MINCHO) fn_font="$FONT_MINCHO" ;; |
*) |
warn "\$font_filename was overridden with an incorrect value, using default." |
fn_font="$font_heading" |
;; |
esac |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
convert \ |
\( \ |
-size $(( ${headwidth} -18 ))x1 "xc:$bg_heading" +size \ |
-font "$font_heading" -pointsize "$pts_meta" \ |
-background "$bg_heading" -fill "$fg_heading" \ |
\( \ |
-gravity West \ |
\( label:"Filename:" \ |
-font "$fn_font" label:"$(basename "$f")" +append \ |
\) \ |
-font "$font_heading" \ |
label:"File size: $(get_pretty_size "$f")" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$bg_heading" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x34 -gravity Center "xc:$bg_sign" \ |
-font "$font_sign" -pointsize "$pts_sign" \ |
-fill "$fg_sign" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
cleanup |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# unit_test() |
unit_test() { |
local t op val ret comm retval=0 |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
local TESTS=( |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
"pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
) |
for t in "${TESTS[@]}" ; do |
# Note the use of ! as separator, this is because # and / are used in |
# many of the inputs |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=$($op) || true |
if [ "$ret" != "$val" ] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
let 'retval++,1' # The ,1 ensures let doesn't fail |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
local TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest 1>0 -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=$(sed 's!.* #!!g' <<<$t) |
# Expected value |
val=$(sed -r "s!.* (.*) #$comm\$!\1!g"<<<$t) |
op=$(sed "s! $val #$comm\$!!g" <<<$t) |
if [ -z "$comm" ]; then |
comm=unnamed |
fi |
ret=0 |
$op || { |
ret=$? |
} |
if [ $val -eq $ret ]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
let 'retval++,1' |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
inf "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or |
fractions. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-E|--end_offset <arg> This time is ignored, from the end of the video. Same |
format as -i. This value is not used when a explicit |
ending time is set. By default it is $DEFAULT_END_OFFSET. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
-Wo Workaround: Change ffmpeg's arguments order, might |
work with some files that fail otherwise. |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-O|--override <arg> Use it to override a variable (see the homepage for |
more details). Format accepted is 'variable=value' (can |
also be quoted -variable="some value"- and can take an |
internal variable too -variable="\$SOME_VAR"-). |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-Ij|-Ik |
--mincho Use the kana/kanji/hiragana font (EXPERIMENTAL) might |
also work partially with Hangul and Cyrillic. |
-k <arg> |
--funky <arg> Funky modes: |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
They're random in nature so using the same funky mode |
twice will usually lead to quite different results. |
Currently available "funky modes": |
"overlap": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
"rotate": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
"photoframe": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
"polaroid": Use '-kp' or '--funky polaroid' |
Combination of rotate, photoframe and overlap. |
Same as -kr -ko -kf. |
"film": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
"random": Use '-kx' or '--funky random' |
Randomizes colours and fonts. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Help / Info |
#### Execution starts here #### |
# If tput isn't found simply ignore tput commands |
# (no colour support) |
# Important to do it before any message can be thrown |
if ! type -pf tput >/dev/null ; then |
tput() { cat >/dev/null <<<"$1"; } |
warn "tput wasn't found. Coloured feedback disabled." |
fi |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::U::qAO:I::k:W:E: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,end_offset:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Incorrect interval format. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" == "0" ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
# -U accepts an optiona argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [ "$2" ]; then # With argument, special handling |
if [ "$2" != "0" ]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
anonymous_mode=1 |
shift |
else # No argument, default handling (try to guess real name) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
fi |
;; |
--anonymous) anonymous_mode=1 ;; # Same as -U0 |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-E|--end_offset) |
if ! end_offset=$(get_interval "$2") ; then |
error "End offset must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
--mincho) font_filename=$FF_MINCHO ;; |
-I) # -I technically takes an optional argument (for future alternative |
# fonts) although it is documented as a two-letter option |
# Don't relay on using -I though, if I ever add a new alternative font |
# I might not allow it anymore |
if [ "$2" ] ; then |
# If an argument is passed, test it is one of the known ones |
case "$2" in |
k|j) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
fi |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
font_filename=$FF_MINCHO; |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-W) # Workaround mode, see wa_ss_* declarations at the start for details |
if [ "$2" != "o" ]; then |
error "Wrong argument. Use -Wo instead of -W$2." |
exit $EX_USAGE |
fi |
wa_ss_af='-ss ' wa_ss_be='' |
shift |
;; |
-k|--funky) # Funky modes |
case $(tolower "$2") in |
p|polaroid) # Same as overlap + rotate + photoframe |
inf "Changed to polaroid funky mode." |
FILTERS_IND+=( 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible |
grav_timestamp=NorthWest |
;; |
o|overlap) # Random overlap mode |
CSHEET_DELEGATE='csheet_overlap' |
grav_timestamp=NorthWest |
;; |
r|rotate) # Random rotation |
FILTERS_IND+=( 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
FILTERS_IND+=( 'filt_photoframe' ) |
;; |
i|film) |
inf "Enabled film mode." |
FILTERS_IND+=( 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Enabled random colours and fonts." |
randomize_look |
;; |
*) |
error "Unknown funky mode. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
-D) # Repeat to just test consistency |
if [ $DEBUGGED -gt 0 ]; then exit ; fi |
inf "Testing internal consistency..." |
unit_test |
if [ $? -eq 0 ]; then |
warn "All tests passed" |
else |
error "Some tests failed!" |
fi |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
title="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# }}} # Command line parsing |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# vim:set ts=4 ai foldmethod=marker: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.7a |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.1a/vcs |
---|
0,0 → 1,723 |
#!/bin/bash |
# $Rev$ $Date$ |
declare -r VERSION="1.0.1a" |
# |
# History: |
# |
# 1.0.1a: |
# * Print output filename |
# * Added manual mode (all timestamps provided by user) |
# * More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
# * BUGFIX: Discard repeated timestamps |
# * Added "set -e". TODO: Add more verbose error messages when called |
# programs fail. |
# * Added basic support for a user configuration file. |
# |
# 1.0a: (2007-04-10) |
# * First release keeping track of history |
# * Put vcs' url in the signature |
# * Use system username in signature |
# * Added --shoehorn (you get the idea, right?) to feed extra commands to |
# the cappers. Lowelevel and not intended to be used anyway :P |
# * When just a vidcap is requested, take it from the middle of the video |
# * Added -H|--height |
# |
# 0.99.1a: Interim version, renamed to 1.0a |
# |
# 0.99a: |
# * Added shadows |
# * More colourful headers |
# * Easier change of colours/fonts |
# |
# 0.5a: * First usable version |
# 0.1: * First proof of concept |
set -e |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# BG_META=gray # Make the heading gray |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Constants {{{ |
# see $METHOD |
declare -ri METHOD_MPLAYER=1 METHOD_FFMPEG=2 |
# See $derive_from |
declare -ri INTERVAL=1 NUMCAPS=3 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE=<<EOT |
with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/> |
EOT |
# }}} # End of constants |
# Override-able variables {{{ |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
# Text before the user name in the signature |
declare USER_SIGNATURE="Preview created by" |
# By default sign as the system's username |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i derive_from=$INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
declare -i METHOD=$METHOD_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare OUTFMT=png # ImageMagick decides the type from the extension |
declare -i OUTPUT_QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_META=YellowGreen # Background for meta info (size, codec...) |
declare BG_SIGN=SandyBrown # Background for signature |
declare FG_META=black # Font colour for meta info box |
declare FG_SIGN=black # Font colour for signature |
declare FG_STAMPS=white # Font colour for timestamps |
# Fonts, see convert -list type to get the list |
declare FONT_STAMPS=courier # Used for timestamps behind the thumbnails |
declare FONT_META=helvetica # Used for meta info box |
declare FONT_SIGN=$FONT_META # Used for the signature box |
# Font sizes, in points |
declare PS_STAMPS=18 # Used for the timestamps |
declare PS_META=16 # Used for the meta info box |
declare PS_SIGN=11 # Used for the signature |
# See --shoehorn |
declare shoehorned= |
# }}} # End of override-able variables |
# Options and other internal usage variables, no need to mess with this! |
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (= numsecs / numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (= numsecs / interval) |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Stamps added to the calculated ones (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=2 # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
load_config() { |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'USER_SIGNATURE' |
'BG_.*' |
'FONT_.*' |
'PS_.*' |
'FG_.*' |
'OUTPUT_QUALITY' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'METHOD' |
'OUTFMT' |
'shoehorned' |
'derive_from' |
) |
if [ ! -f "$CFGFILE" ]; then return 0 ; fi |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
while read line ; do # auto variable $line |
# Don't allow ';', FIXME: dunno how secure that really is |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$line" ; then |
continue |
fi |
if ! egrep -q "^($compregex)=" <<<"$line" ; then |
continue |
fi |
# FIXME: Only print in verbose mose |
# FIXME: Only for really overridden ones |
echo "Overridden variable" $(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<$line) |
eval $line |
done <$CFGFILE |
} |
# {{{ # Convenience functions |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
return $? |
} |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
# Only allowed characters |
if ! grep -q '[0-9smh]' <<<"$s"; then |
return 1; |
fi |
# FIXME: Find some cleaner way |
local i= c= num= sum=0 |
for i in $(seq 0 $(( ${#s} - 1)) ); do |
c=${s:$i:1} |
if is_number $c ; then |
num+=$c |
else |
case $c in |
h) num=$(($num * 3600)) ;; |
m) num=$(($num * 60)) ;; |
s) ;; |
*) |
return 2 |
;; |
esac |
sum=$(($sum + $num)) |
num= |
fi |
done |
# If last element was a number, it's seconds and they weren't added |
if is_number $c ; then |
sum=$(( $sum + $num )) |
fi |
echo $sum |
return 0 |
} |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
pretty_stamp() { |
if ! is_number "$1" ; then return 1 ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with -1, -2, ... |
safe_rename() { |
local from="$1" |
local to="$2" |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
local n=1 |
while [ -f "$to" ]; do |
to="$(basename "$2" .$ext)-$n.$ext" |
n=$(( $n + 1)) |
done |
mv "$from" "$to" |
echo "$to" >&2 |
} |
test_programs() { |
for prog in mplayer convert montage ffmpeg ; do |
type -pf "$prog" >/dev/null |
local retval=$? |
if [ $retval -ne 0 ] ; then |
error "Required program $prog not found!" |
return $retval |
fi |
done |
} |
# Print some text to stderr |
error() { |
echo "$1" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return 100 |
fi |
echo "Processing $f..." >&2 |
# Meta data extraction |
# Don't change the -vc as it would affect $vdec |
local mplayer_cache=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) # FourCC |
local acodec=$(grep ID_AUDIO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) |
local vdec=$(grep ID_VIDEO_CODEC <<<"$mplayer_cache" | cut -d'=' -f2) # Decoder |
local width=$(grep ID_VIDEO_WIDTH <<<"$mplayer_cache" | cut -d'=' -f2) |
local height=$(grep ID_VIDEO_HEIGHT <<<"$mplayer_cache" | cut -d'=' -f2) |
local fps=$(grep ID_VIDEO_FPS <<<"$mplayer_cache" | cut -d'=' -f2) |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=$height |
fi |
local numsecs=$(grep ID_LENGTH <<<"$mplayer_cache"| cut -d'=' -f2 | cut -d. -f1) |
if ! is_number $numsecs ; then |
error "Internal error!" |
return 15 |
fi |
local nc=$numcaps |
local in=$interval |
# Start bound: |
local startsec=0 |
if [ $startsec -lt $fromtime ]; then |
startsec=$fromtime |
fi |
# End bound: |
local endsec=$numsecs |
if [ $totime -ne -1 ] && [ $endsec -gt $totime ]; then |
endsec=$totime |
fi |
if [ $startsec -ne 0 ] || [ $endsec -ne $numsecs ]; then |
echo "Restricting to range [$startsec..$endsec]" >&2 |
fi |
local delta=$(( $endsec - $startsec )) |
# FIXME: the total # of caps is currently broken when using -f and -t |
# Note that when numcaps mandates the interval is obtained from |
# the actually allowed secods (hence it is not movie_length / numcaps ) |
# Adjust interval/numcaps: |
if [ "$derive_from" -eq "$INTERVAL" ]; then |
# Interval rules => it doesn't change |
nc=$(( $delta / $interval )) |
# If a multiple, an extra vidcap is generated (at the last second) |
if [ $(( $delta % $interval )) -eq 0 ]; then |
nc=$(( $nc + 1 )) |
fi |
elif [ "$derive_from" -eq "$NUMCAPS" ]; then |
# Numcaps rules => it doesn't change |
if [ $numcaps -eq 1 ]; then # If just one cap, center it |
in=$(( $numsecs / 2 )) |
else |
in=$(( $numsecs / $numcaps )) |
fi |
else |
error "Internal error!" |
return 145 |
fi |
# Let's try to make some sense... |
# Minimum interval allowance: |
if [ $in -gt $numsecs ]; then |
error "The interval is longer than the video length." |
error "Use a lower interval or numcaps instead." |
error "Skipping \"$f\"." |
return 16 |
fi |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
# Tempdir |
local dir=$(mktemp -d -p . vcs.XXXXXX) |
if [ "$?" -ne 0 ]; then |
error "Error creating temporary directory" |
return 17 |
fi |
local n= |
# Get the stamps (if in auto mode)... |
local stamps=( ) |
if [ $manual_mode -ne 1 ]; then |
n=$(( $startsec + $in )) |
stamps=( ${initial_stamps[*]} $n ) |
while [ $n -le $endsec ]; do |
n=$(( $n + $in )) |
if [ $n -gt $endsec ]; then break; fi |
stamps=( ${stamps[*]} $n ) |
done |
else |
stamps=( ${initial_stamps[*]} ) |
fi |
n=1 |
local p="" |
local cap="" |
local montage_command="montage -font $FONT_STAMPS -pointsize $PS_STAMPS \ |
-gravity SouthEast -fill white " |
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png' -d .) |
# Let's reorder the stamps, this away user-added stamps get their correct |
# position also remove duplicates. Note AFAIK sort only sorts lines, that's |
# why y replace spaces by newlines. |
# |
# Note that stamps keeps being an array and stamps[1..N] still hold their |
# old values, although it's treated as a string after this |
stamps=$( sed 's/ /\n/g' <<<"${stamps[*]}" | sort -n | uniq ) |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
exit 134 |
fi |
local NUMSTAMPS=$(wc -w <<<"$stamps") |
# TODO: Aspect ratio |
for stamp in $stamps; do |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
if [ $stamp -gt $numsecs ]; then continue; fi |
echo "Generating capture #${n}/${NUMSTAMPS}..." >&2 |
if [ $METHOD -eq $METHOD_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $METHOD -eq $METHOD_FFMPEG ]; then |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return 142 |
fi || { |
error "The capturing program failed!" |
return 143 |
} |
p=$(pad 6 $stamp).png |
# mv 00000001.png $dir/$p |
n=$(( $n + 1 )) |
cap=$dir/$p |
# Add the timestamp to each vidcap, doing it hear is much powerful/simple |
# than with the next montage command |
convert -box '#000000aa' \ |
-fill $FG_STAMPS -pointsize $PS_STAMPS -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
$VIDCAPFILE "$cap" |
montage_command+=" $cap" |
done |
rm -f $VIDCAPFILE |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
montage_command+=" -geometry x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $FONT_META -fill $FG_META -title '$title'" |
fi |
montage_command+=" $output" |
echo "Composing contact sheet..." >&2 |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
mv "$output" . 2>/dev/null || true |
# Codec "prettyfication" |
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
avc1) vcodec="MPEG-4 AVC" ;; |
wmv2) vcodec="WMV 8" ;; # WMV2 is v8 |
esac |
if [ "$vdec" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "$vdec" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in |
85) acodec='MPEG-1 Layer III (MP3)' ;; |
80) acodec='MPEG-1 Layer II (MP2)' ;; |
mp4a) acodec='MPEG-4 AAC' ;; |
353) acodec='WMA 2' ;; |
"") acodec="no audio" ;; |
esac |
local meta="Filename: $f |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "$numsecs")" |
local meta2="Dimensions: ${width}x${height} |
Format: $vcodec / $acodec |
FPS: $fps" |
local signature="$USER_SIGNATURE $user |
$PROGRAM_SIGNATURE" |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $FONT_META -pointsize $PS_META \ |
-background $BG_META -fill $FG_META -splice 0x$(( $PS_META * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $FONT_SIGN -pointsize $PS_SIGN \ |
-background $BG_SIGN -splice 0x34+0-0 \ |
-fill $FG_SIGN -draw "text 10,3 '$signature'" "$output" "$output" |
rm -rf $dir/ |
if [ $OUTFMT != "png" ]; then |
local newout="$(basename "$output" .png).$OUTFMT" |
convert -quality $OUTPUT_QUALITY "$output" "$newout" |
rm "$output" |
output="$newout" |
fi |
echo -n "Output wrote to " >&2 |
safe_rename "$output" "$(basename "$f").$OUTFMT" |
} |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. An optional unit can be |
used (case-insensitive) , e.g.: |
Seconds: 90 |
Minutes: 3m |
Hours: 1h |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-S|--stamp <arg> Add the image found at the timestamp "arg", same format |
as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-j|--jpeg Output in jpeg (by default output is in png). |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 minutes: |
\$ $P -i 3m input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
Quirks: |
Currently MPEG handling seems to be broken. |
EOF |
} |
# }}} # Core functionality |
# Test requirements |
test_programs || exit 54 |
load_config |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# FIXME: use username / no name at all with -u noarg, and not -u |
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c:m \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:,manual" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a number (given $2)" |
exit 68 |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
derive_from=$INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of caps must be a number! (given $2)" |
exit 68 |
fi |
numcaps="$2" |
derive_from=$NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid interval" |
exit 68 |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid interval" |
exit 68 |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length" |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid interval" |
exit 68 |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-j|--jpeg) OUTFMT=jpg ;; |
-h|--help) show_help ; exit 0 ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) METHOD=$METHOD_FFMPEG ;; |
-M) METHOD=$METHOD_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a number (given $2)" |
exit 68 |
fi |
th_height="$2" |
shift |
;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a number (given $2)" |
exit 68 |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit 76 ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit 67 |
fi |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit 68 |
fi |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.1a |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
/ATTIC/video-contact-sheet/tags/1.0a/vcs |
---|
0,0 → 1,595 |
#!/bin/bash |
# $Rev$ $Date$ |
# |
# History: |
# |
# 1.0a: |
# * First release keeping track of history |
# * Put vcs' url in the signature |
# * Use system username in signature |
# * Added --shoehorn (you get the idea, right?) to feed extra commands to |
# the cappers. Lowelevel and not intended to be used anyway :P |
# * When just a vidcap is requested, take it from the middle of the video |
# * Added -H|--height |
# |
# 0.99.1a: Interim version, renamed to 1.0a |
# |
# 0.99a: |
# * Added shadows |
# * More colourful headers |
# * Easier change of colours/fonts |
# |
# 0.5a: * First usable version |
# 0.1: * First proof of concept |
declare -r VERSION="0.99.1a" |
# Options |
declare -i interval=300 # Interval of captures (= numsecs / numcaps) |
declare -i numcaps=16 # Number of captures (= numsecs / interval) |
declare user=$(id -un) |
declare -r USER_SIGNATURE="Preview created by" |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet so tweak them to your liking |
# |
declare -i OUTPUT_QUALITY=92 # Output image quality (only affects the final image, |
# and only in lossy formats) |
# Colours, see convert -list color |
declare -r BG_META=YellowGreen # Background for meta info (size, codec...) |
declare -r BG_SIGN=SandyBrown # Background for signature |
declare -r FG_META=black # Font colour for meta info box |
declare -r FG_SIGN=black # Font colour for signature |
declare -r FG_STAMPS=white # Font colour for timestamps |
# Fonts, see convert -list type |
declare -r FONT_STAMPS=courier # Used for timestamps behind the thumbnails |
declare -r FONT_META=helvetica # Used for meta info box |
declare -r FONT_SIGN=$FONT_META # Used for the signature box |
# Font sizes, in points |
declare -i PS_STAMPS=18 # Used for the timestamps |
declare -i PS_META=16 # Used for the meta info box |
declare -i PS_SIGN=11 # Used for the signature |
# Constants and other internal usage variables, no need to mess with this! |
# Which of the two methods should be used to guess the number of thumbnails |
declare -ri INTERVAL=1 NUMCAPS=3 |
declare -i derive_from=$INTERVAL |
declare -ri DEFAULT_INTERVAL=$interval |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
declare -ri METHOD_MPLAYER=1 |
declare -ri METHOD_FFMPEG=2 |
declare -i METHOD=$METHOD_FFMPEG |
# Internal variables: |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare initial_stamps=( ) # Stamps added to the calculated ones (see -S) |
declare OUTFMT=png |
declare shoehorned= # See --shoehorn |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=2 # Number of output columns |
# {{{ # Convenience functions |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
return $? |
} |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
local len_n=$(( ${#s} - 1 )) # Length of the theoretical numeric part |
local u=${s:$len_n} |
local n=${s:0:$len_n} |
local i=0 |
if ! is_number "$n" ; then return 1 ; fi |
case "$u" in |
s) i=$n ;; |
m) i=$((n * 60)) ;; |
h) i=$((n * 3600)) ;; |
*) return 2 ; break ;; |
esac |
echo $i |
} |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
pretty_stamp() { |
if ! is_number "$1" ; then return 1 ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with -1, -2, ... |
safe_rename() { |
local from="$1" |
local to="$2" |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
local n=1 |
while [ -f "$to" ]; do |
to="$(basename "$2" .$ext)-$n.$ext" |
n=$(( $n + 1)) |
done |
mv "$from" "$to" |
} |
test_programs() { |
for prog in mplayer convert montage ffmpeg ; do |
type -pf "$prog" >/dev/null |
local retval=$? |
if [ $retval -ne 0 ] ; then |
error "Required program $prog not found!" |
return $retval |
fi |
done |
} |
# Print some text to stderr |
error() { |
echo "$1" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return 100 |
fi |
echo "Processing $f..." >&2 |
# Meta data extraction |
# Don't change the -vc as it would affect $vdec |
local mplayer_cache=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) # FourCC |
local acodec=$(grep ID_AUDIO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) |
local vdec=$(grep ID_VIDEO_CODEC <<<"$mplayer_cache" | cut -d'=' -f2) # Decoder |
local width=$(grep ID_VIDEO_WIDTH <<<"$mplayer_cache" | cut -d'=' -f2) |
local height=$(grep ID_VIDEO_HEIGHT <<<"$mplayer_cache" | cut -d'=' -f2) |
local fps=$(grep ID_VIDEO_FPS <<<"$mplayer_cache" | cut -d'=' -f2) |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=$height |
fi |
local numsecs=$(grep ID_LENGTH <<<"$mplayer_cache"| cut -d'=' -f2 | cut -d. -f1) |
if ! is_number $numsecs ; then |
error "Internal error!" |
return 15 |
fi |
local nc=$numcaps |
local in=$interval |
# Start bound: |
local startsec=0 |
if [ $startsec -lt $fromtime ]; then |
startsec=$fromtime |
fi |
# End bound: |
local endsec=$numsecs |
if [ $totime -ne -1 ] && [ $endsec -gt $totime ]; then |
endsec=$totime |
fi |
if [ $startsec -ne 0 ] || [ $endsec -ne $numsecs ]; then |
echo "Restricting to range [$startsec..$endsec]" >&2 |
fi |
local delta=$(( $endsec - $startsec )) |
# FIXME: the total # of caps is currently broken when using -f and -t |
# Note that when numcaps mandates the interval is obtained from |
# the actually allowed secods (hence it is not movie_length / numcaps ) |
# Adjust interval/numcaps: |
if [ "$derive_from" -eq "$INTERVAL" ]; then |
# Interval rules => it doesn't change |
nc=$(( $delta / $interval )) |
# If a multiple, an extra vidcap is generated (at the last second) |
if [ $(( $delta % $interval )) -eq 0 ]; then |
nc=$(( $nc + 1 )) |
fi |
elif [ "$derive_from" -eq "$NUMCAPS" ]; then |
# Numcaps rules => it doesn't change |
if [ $numcaps -eq 1 ]; then # If just one cap, center it |
in=$(( $numsecs / 2 )) |
else |
in=$(( $numsecs / $numcaps )) |
fi |
else |
error "Internal error!" |
return 145 |
fi |
# Let's try to make some sense... |
# Minimum interval allowance: |
if [ $in -gt $numsecs ]; then |
error "The interval is longer than the video length." |
error "Use a lower interval or numcaps instead." |
error "Skipping \"$f\"." |
return 16 |
fi |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
# Tempdir |
local dir=$(mktemp -d -p . vcs.XXXXXX) |
if [ "$?" -ne 0 ]; then |
error "Error creating temporary directory" |
return 17 |
fi |
# Get the stamps... |
local n=$(( $startsec + $in )) |
local stamps=( ${initial_stamps[*]} $n ) |
while [ $n -le $endsec ]; do |
n=$(( $n + $in )) |
if [ $n -gt $endsec ]; then break; fi |
stamps=( ${stamps[*]} $n ) |
done |
n=1 |
local p="" |
local cap="" |
local montage_command="montage -font $FONT_STAMPS -pointsize $PS_STAMPS \ |
-gravity SouthEast -fill white " |
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png' -d .) |
# Let's reorder the stamps, this away user-added stamps get their correct position |
local temp="" |
for stamp in ${stamps[*]} ; do |
temp+="$stamp\n" |
done |
stamps=$(echo -e $temp | sort -n) |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
exit 134 |
fi |
# TODO: Aspect ratio |
for stamp in $stamps; do |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
if [ $stamp -gt $numsecs ]; then continue; fi |
echo "Generating capture #${n}/${#stamps[*]}..." >&2 |
if [ $METHOD -eq $METHOD_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $METHOD -eq $METHOD_FFMPEG ]; then |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return 142 |
fi |
p=$(pad 6 $stamp).png |
# mv 00000001.png $dir/$p |
n=$(( $n + 1 )) |
cap=$dir/$p |
# Add the timestamp to each vidcap, doing it hear is much powerful/simple |
# than with the next montage command |
convert -box '#000000aa' \ |
-fill $FG_STAMPS -pointsize $PS_STAMPS -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
$VIDCAPFILE "$cap" |
montage_command+=" $cap" |
done |
rm -f $VIDCAPFILE |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
montage_command+=" -geometry x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $FONT_META -fill $FG_META -title '$title'" |
fi |
montage_command+=" $output" |
echo "Composing contact sheet..." >&2 |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
mv "$output" . 2>/dev/null || true |
# Codec "prettyfication" |
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
avc1) vcodec="MPEG-4 AVC" ;; |
esac |
if [ "$vdec" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "$vdec" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in |
85) acodec='MPEG-1 Layer III (MP3)' ;; |
80) acodec='MPEG-1 Layer II (MP2)' ;; |
mp4a) acodec='MPEG-4 AAC' ;; |
"") acodec="no audio" ;; |
esac |
local meta="Filename: $f |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "$numsecs")" |
local meta2="Dimensions: ${width}x${height} |
Format: $vcodec / $acodec |
FPS: $fps" |
local signature="$USER_SIGNATURE $user |
$PROGRAM_SIGNATURE" |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $FONT_META -pointsize $PS_META \ |
-background $BG_META -fill $FG_META -splice 0x$(( $PS_META * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $FONT_SIGN -pointsize $PS_SIGN \ |
-background $BG_SIGN -splice 0x34+0-0 \ |
-fill $FG_SIGN -draw "text 10,3 '$signature'" "$output" "$output" |
rm -rf $dir/ |
if [ $OUTFMT != "png" ]; then |
local newout="$(basename "$output" .png).$OUTFMT" |
convert -quality $OUTPUT_QUALITY "$output" "$newout" |
rm "$output" |
output="$newout" |
fi |
safe_rename "$output" "$(basename "$f").$OUTFMT" |
} |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. An optional unit can be |
used (case-insensitive) , e.g.: |
Seconds: 90 |
Minutes: 3m |
Hours: 1h |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-S|--stamp <arg> Add the image found at the timestamp "arg", same format |
as -i. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-j|--jpeg Output in jpeg (by default output is in png). |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 minutes: |
\$ $P -i 3m input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
Quirks: |
Currently MPEG handling seems to be broken. |
EOF |
} |
# }}} # Core functionality |
# Test requirements |
test_programs || exit 54 |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# FIXME: use username / no name at all with -u noarg, and not -u |
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a number (given $2)" |
exit 68 |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
derive_from=$INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of caps must be a number! (given $2)" |
exit 68 |
fi |
numcaps="$2" |
derive_from=$NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid interval" |
exit 68 |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid interval" |
exit 68 |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length" |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid interval" |
exit 68 |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-j|--jpeg) OUTFMT=jpg ;; |
-h|--help) show_help ; exit 0 ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) METHOD=$METHOD_FFMPEG ;; |
-M) METHOD=$METHOD_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a number (given $2)" |
exit 68 |
fi |
th_height="$2" |
shift |
;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a number (given $2)" |
exit 68 |
fi |
cols="$2" |
shift |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit 76 ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit 67 |
fi |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0a |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/0.99a:r261 |
/ATTIC/video-contact-sheet/tags/0.99a/vcs |
---|
0,0 → 1,526 |
#!/bin/bash |
# $Rev$ $Date$ |
declare -r VERSION="0.99a" |
# Options |
declare -i interval=300 # Interval of captures (= numsecs / numcaps) |
declare -i numcaps=16 # Number of captures (= numsecs / interval) |
declare user=$(id -un) |
declare -r USER_SIGNATURE="Preview created by" |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION}" |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet so tweak them to your liking |
# |
declare -i OUTPUT_QUALITY=92 # Output image quality (only affects the final image, |
# and only in lossy formats) |
# Colours, see convert -list color |
declare -r BG_META=YellowGreen # Background for meta info (size, codec...) |
declare -r BG_SIGN=SandyBrown # Background for signature |
declare -r FG_META=black # Font colour for meta info box |
declare -r FG_SIGN=black # Font colour for signature |
declare -r FG_STAMPS=white # Font colour for timestamps |
# Fonts, see convert -list type |
declare -r FONT_STAMPS=courier # Used for timestamps behind the thumbnails |
declare -r FONT_META=helvetica # Used for meta info box |
declare -r FONT_SIGN=$FONT_META # Used for the signature box |
# Font sizes, in points |
declare -i PS_STAMPS=18 # Used for the timestamps |
declare -i PS_META=16 # Used for the meta info box |
declare -i PS_SIGN=11 # Used for the signature |
# Constants and other internal usage variables, no need to mess with this! |
# Which of the two methods should be used to guess the number of thumbnails |
declare -ri INTERVAL=1 NUMCAPS=3 |
declare -i derive_from=$INTERVAL |
declare -ri DEFAULT_INTERVAL=$interval |
# Internal variables: |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare initial_stamps=( ) # Stamps added to the calculated ones (see -S) |
declare OUTFMT=png |
# {{{ # Convenience functions |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
return $? |
} |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
local len_n=$(( ${#s} - 1 )) # Length of the theoretical numeric part |
local u=${s:$len_n} |
local n=${s:0:$len_n} |
local i=0 |
if ! is_number "$n" ; then return 1 ; fi |
case "$u" in |
s) i=$n ;; |
m) i=$((n * 60)) ;; |
h) i=$((n * 3600)) ;; |
*) return 2 ; break ;; |
esac |
echo $i |
} |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
pretty_stamp() { |
if ! is_number "$1" ; then return 1 ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with -1, -2, ... |
safe_rename() { |
local from="$1" |
local to="$2" |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
local n=1 |
while [ -f "$to" ]; do |
to="$(basename "$2" .$ext)-$n.$ext" |
n=$(( $n + 1)) |
done |
mv "$from" "$to" |
} |
test_programs() { |
for prog in mplayer convert montage ffmpeg ; do |
type -pf "$prog" >/dev/null |
local retval=$? |
if [ $retval -ne 0 ] ; then |
error "Required program $prog not found!" |
return $retval |
fi |
done |
} |
# Print some text to stderr |
error() { |
echo "$1" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
process() { |
local f=$1 |
local COLS=2 |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return 100 |
fi |
echo "Processing $f..." >&2 |
# Meta data extraction |
# Don't change the -vc as it would affect $vdec |
local mplayer_cache=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) # FourCC |
local acodec=$(grep ID_AUDIO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) |
local vdec=$(grep ID_VIDEO_CODEC <<<"$mplayer_cache" | cut -d'=' -f2) # Decoder |
local width=$(grep ID_VIDEO_WIDTH <<<"$mplayer_cache" | cut -d'=' -f2) |
local height=$(grep ID_VIDEO_HEIGHT <<<"$mplayer_cache" | cut -d'=' -f2) |
local fps=$(grep ID_VIDEO_FPS <<<"$mplayer_cache" | cut -d'=' -f2) |
# Vidcap/Thumbnail width TODO: allow user interaction |
local th_width=$width |
local numsecs=$(grep ID_LENGTH <<<"$mplayer_cache"| cut -d'=' -f2 | cut -d. -f1) |
if ! is_number $numsecs ; then |
error "Internal error!" |
return 15 |
fi |
local nc=$numcaps |
local in=$interval |
# Start bound: |
local startsec=0 |
if [ $startsec -lt $fromtime ]; then |
startsec=$fromtime |
fi |
# End bound: |
local endsec=$numsecs |
if [ $totime -ne -1 ] && [ $endsec -gt $totime ]; then |
endsec=$totime |
fi |
local delta=$(( $endsec - $startsec )) |
# FIXME: the total # of caps is currently broken when using -f and -t |
# Note that when numcaps mandates the interval is obtained from |
# the actually allowed secods (hence it is not movie_length / numcaps ) |
# Adjust interval/numcaps: |
if [ "$derive_from" -eq "$INTERVAL" ]; then |
# Interval rules => it doesn't change |
nc=$(( $delta / $interval )) |
# If a multiple, an extra vidcap is generated (at the last second) |
if [ $(( $delta % $interval )) -eq 0 ]; then |
nc=$(( $nc + 1 )) |
fi |
elif [ "$derive_from" -eq "$NUMCAPS" ]; then |
# Numcaps rules => it doesn't change |
in=$(( $numsecs / $numcaps )) |
else |
error "Internal error!" |
return 145 |
fi |
# Let's try to make some sense... |
# Minimum interval allowance: |
if [ $in -gt $numsecs ]; then |
error "The interval is longer than the video length." |
error "Use a lower interval or numcaps instead." |
error "Skipping \"$f\"." |
return 16 |
fi |
# Contact sheet minimum cols: |
if [ $nc -lt $COLS ]; then |
COLS=$nc |
fi |
# Tempdir |
local dir=$(mktemp -d -p . vcs.XXXXXX) |
if [ "$?" -ne 0 ]; then |
error "Error creating temporary directory" |
return 17 |
fi |
# Get the stamps... |
local n=$(( $startsec + $in )) |
local stamps=( ${initial_stamps[*]} $n ) |
while [ $n -le $endsec ]; do |
n=$(( $n + $in )) |
if [ $n -gt $endsec ]; then break; fi |
stamps=( ${stamps[*]} $n ) |
done |
n=1 |
local p="" |
local cap="" |
local montage_command="montage -font $FONT_STAMPS -pointsize $PS_STAMPS \ |
-gravity SouthEast -fill white " |
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png' -d .) |
# Let's reorder the stamps, this away user-added stamps get their correct position |
local temp="" |
for stamp in ${stamps[*]} ; do |
temp+="$stamp\n" |
done |
stamps=$(echo -e $temp | sort -n) |
METHOD=2 |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
exit 134 |
fi |
# TODO: Aspect ratio |
for stamp in $stamps; do |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
if [ $stamp -gt $numsecs ]; then continue; fi |
echo "Generating capture #${n}/${#stamps[*]}..." >&2 |
if [ $METHOD -eq 1 ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp "$f" >/dev/null 2>&1 |
elif [ $METHOD -eq 2 ]; then |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return 142 |
fi |
p=$(pad 6 $stamp).png |
# mv 00000001.png $dir/$p |
n=$(( $n + 1 )) |
cap=$dir/$p |
# Add the timestamp to each vidcap, doing it hear is much powerful/simple |
# than with the next montage command |
convert -box '#000000aa' \ |
-fill $FG_STAMPS -pointsize $PS_STAMPS -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
$VIDCAPFILE "$cap" |
montage_command+=" $cap" |
done |
rm -f $VIDCAPFILE |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
montage_command+=" -geometry ${th_width}x+10+5 -tile ${COLS}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $FONT_META -fill $FG_META -title '$title'" |
fi |
montage_command+=" $output" |
echo "Composing contact sheet..." >&2 |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
mv "$output" . 2>/dev/null || true |
# Codec "prettyfication" |
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
avc1) vcodec="MPEG-4 AVC" ;; |
esac |
if [ "$vdec" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "$vdec" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in |
85) acodec='MPEG-1 Layer III (MP3)' ;; |
80) acodec='MPEG-1 Layer II (MP2)' ;; |
mp4a) acodec='MPEG-4 AAC' ;; |
"") acodec="no audio" ;; |
esac |
local meta="Filename: $f |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "$numsecs")" |
local meta2="Dimensions: ${width}x${height} |
Format: $vcodec / $acodec |
FPS: $fps" |
local signature="$USER_SIGNATURE $user |
$PROGRAM_SIGNATURE" |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $FONT_META -pointsize $PS_META \ |
-background $BG_META -fill $FG_META -splice 0x$(( $PS_META * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $FONT_SIGN -pointsize $PS_SIGN \ |
-background $BG_SIGN -splice 0x34+0-0 \ |
-fill $FG_SIGN -draw "text 10,3 '$signature'" "$output" "$output" |
rm -rf $dir/ |
if [ $OUTFMT != "png" ]; then |
local newout="$(basename "$output" .png).$OUTFMT" |
convert -quality $OUTPUT_QUALITY "$output" "$newout" |
rm "$output" |
output="$newout" |
fi |
safe_rename "$output" "$(basename "$f").$OUTFMT" |
} |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. An optional unit can be |
used (case-insensitive) , e.g.: |
Seconds: 90 |
Minutes: 3m |
Hours: 1h |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-S|--stamp <arg> Add the image found at the timestamp "arg", same format |
as -i. |
-j|--jpeg Output in jpeg (by default output is in png). |
-h|--help Show this text. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 minutes: |
\$ $P -i 3m input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
Quirks: |
Currently MPEG handling seems to be broken. |
EOF |
} |
# }}} # Core functionality |
# Test requirements |
test_programs || exit 54 |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# FIXME: use username / no name at all with -u noarg, and not -u |
TEMP=$(getopt -o i:n:u:T:f:t:S:jh \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a number (given $2)" |
exit 68 |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
derive_from=$INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of caps must be a number! (given $2)" |
exit 68 |
fi |
numcaps="$2" |
derive_from=$NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid interval" |
exit 68 |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid interval" |
exit 68 |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length" |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid interval" |
exit 68 |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-j|--jpeg) OUTFMT=jpg ;; |
-h|--help) show_help ; exit 0 ;; |
--) shift ; break ;; |
*) error "Internal error!" ; exit 76 ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit 67 |
fi |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.2b/vcs |
---|
0,0 → 1,779 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
declare -r VERSION="1.0.2b" |
# |
# History: |
# |
# 1.0.2b: (2007-04-14) |
# * Licensed under LGPL (was unlicensed before) |
# * Renamed variables and constants to me more congruent |
# * Added DEFAULT_COLS |
# * BUGFIX: Fixed program signature |
# * Streamlined error codes |
# * Added cleanup on failure and on delayed cleanup on success |
# * Changed default signature background to SlateGray (blue-ish gray) |
# |
# 1.0.1a: (2007-04-13) |
# * Print output filename |
# * Added manual mode (all timestamps provided by user) |
# * More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
# * BUGFIX: Discard repeated timestamps |
# * Added "set -e". TODO: Add more verbose error messages when called |
# programs fail. |
# * Added basic support for a user configuration file. |
# |
# 1.0a: (2007-04-10) |
# * First release keeping track of history |
# * Put vcs' url in the signature |
# * Use system username in signature |
# * Added --shoehorn (you get the idea, right?) to feed extra commands to |
# the cappers. Lowelevel and not intended to be used anyway :P |
# * When just a vidcap is requested, take it from the middle of the video |
# * Added -H|--height |
# |
# 0.99.1a: Interim version, renamed to 1.0a |
# |
# 0.99a: |
# * Added shadows |
# * More colourful headers |
# * Easier change of colours/fonts |
# |
# 0.5a: * First usable version |
# 0.1: * First proof of concept |
set -e |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Constants {{{ |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# }}} # End of constants |
# Override-able variables {{{ |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps behind the thumbnails |
declare font_heading=helvetica # Used for meta info box |
declare font_sign=$font_heading # Used for the signature box |
# Font sizes, in points |
declare pts_tstamps=18 # Used for the timestamps |
declare pts_meta=16 # Used for the meta info box |
declare pts_sign=11 # Used for the signature |
# See --shoehorn |
declare shoehorned= |
# }}} # End of override-able variables |
# Options and other internal usage variables, no need to mess with this! |
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare -a TEMPSTUFF=( ) # Temporal files |
# Exit codes, same codes as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
load_config() { |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
) |
if [ ! -f "$CFGFILE" ]; then return 0 ; fi |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
while read line ; do # auto variable $line |
# Don't allow ';', FIXME: dunno how secure that really is |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$line" ; then |
continue |
fi |
if ! egrep -q "^($compregex)=" <<<"$line" ; then |
continue |
fi |
# FIXME: Only print in verbose mose |
# FIXME: Only for really overridden ones |
echo "Overridden variable" $(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<$line) |
eval $line |
done <$CFGFILE |
} |
# {{{ # Convenience functions |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
return $? |
} |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
# Only allowed characters |
if ! grep -q '[0-9smh]' <<<"$s"; then |
return $EX_USAGE; |
fi |
# FIXME: Find some cleaner way |
local i= c= num= sum=0 |
for i in $(seq 0 $(( ${#s} - 1)) ); do |
c=${s:$i:1} |
if is_number $c ; then |
num+=$c |
else |
case $c in |
h) num=$(($num * 3600)) ;; |
m) num=$(($num * 60)) ;; |
s) ;; |
*) |
return $EX_SOFTWARE |
;; |
esac |
sum=$(($sum + $num)) |
num= |
fi |
done |
# If last element was a number, it's seconds and they weren't added |
if is_number $c ; then |
sum=$(( $sum + $num )) |
fi |
echo $sum |
return 0 |
} |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
pretty_stamp() { |
if ! is_number "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with -1, -2, ... |
safe_rename() { |
local from="$1" |
local to="$2" |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
local n=1 |
while [ -f "$to" ]; do |
to="$(basename "$2" .$ext)-$n.$ext" |
n=$(( $n + 1)) |
done |
mv "$from" "$to" |
echo "$to" >&2 |
} |
test_programs() { |
for prog in mplayer convert montage ffmpeg ; do |
type -pf "$prog" >/dev/null |
local retval=$? |
if [ $retval -ne 0 ] ; then |
error "Required program $prog not found!" |
return $retval |
fi |
done |
} |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
echo "Cleaning up..." >&2 # TODO: Only in verbose mode |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
exithdlr() { |
cleanup |
} |
# Print some text to stderr |
error() { |
echo "$1" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
echo "Processing $f..." >&2 |
# Meta data extraction |
# Don't change the -vc as it would affect $vdec |
local mplayer_cache=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) # FourCC |
local acodec=$(grep ID_AUDIO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) |
local vdec=$(grep ID_VIDEO_CODEC <<<"$mplayer_cache" | cut -d'=' -f2) # Decoder |
local width=$(grep ID_VIDEO_WIDTH <<<"$mplayer_cache" | cut -d'=' -f2) |
local height=$(grep ID_VIDEO_HEIGHT <<<"$mplayer_cache" | cut -d'=' -f2) |
local fps=$(grep ID_VIDEO_FPS <<<"$mplayer_cache" | cut -d'=' -f2) |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=$height |
fi |
local numsecs=$(grep ID_LENGTH <<<"$mplayer_cache"| cut -d'=' -f2 | cut -d. -f1) |
if ! is_number $numsecs ; then |
error "Internal error!" |
return $EX_SOFTWARE |
fi |
local nc=$numcaps |
local in=$interval |
# Start bound: |
local startsec=0 |
if [ $startsec -lt $fromtime ]; then |
startsec=$fromtime |
fi |
# End bound: |
local endsec=$numsecs |
if [ $totime -ne -1 ] && [ $endsec -gt $totime ]; then |
endsec=$totime |
fi |
if [ $startsec -ne 0 ] || [ $endsec -ne $numsecs ]; then |
echo "Restricting to range [$startsec..$endsec]" >&2 |
fi |
local delta=$(( $endsec - $startsec )) |
# FIXME: the total # of caps is currently broken when using -f and -t |
# Note that when numcaps mandates the interval is obtained from |
# the actually allowed secods (hence it is not movie_length / numcaps ) |
# Adjust interval/numcaps: |
if [ "$timecode_from" -eq "$TC_INTERVAL" ]; then |
# Interval rules => it doesn't change |
nc=$(( $delta / $interval )) |
# If a multiple, an extra vidcap is generated (at the last second) |
if [ $(( $delta % $interval )) -eq 0 ]; then |
nc=$(( $nc + 1 )) |
fi |
elif [ "$timecode_from" -eq "$TC_NUMCAPS" ]; then |
# Numcaps rules => it doesn't change |
if [ $numcaps -eq 1 ]; then # If just one cap, center it |
in=$(( $numsecs / 2 )) |
else |
in=$(( $numsecs / $numcaps )) |
fi |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi |
# Let's try to make some sense... |
# Minimum interval allowance: |
if [ $in -gt $numsecs ]; then |
error "The interval is longer than the video length." |
error "Use a lower interval or numcaps instead." |
error "Skipping \"$f\"." |
return $EX_USAGE |
fi |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
# Tempdir |
local dir=$(mktemp -d -p . vcs.XXXXXX) |
if [ "$?" -ne 0 ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$dir" ) |
local n= |
# Get the stamps (if in auto mode)... |
local stamps=( ) |
if [ $manual_mode -ne 1 ]; then |
n=$(( $startsec + $in )) |
stamps=( ${initial_stamps[*]} $n ) |
while [ $n -le $endsec ]; do |
n=$(( $n + $in )) |
if [ $n -gt $endsec ]; then break; fi |
stamps=( ${stamps[*]} $n ) |
done |
else |
stamps=( ${initial_stamps[*]} ) |
fi |
n=1 |
local p="" |
local cap="" |
local montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png' -d .) |
TEMPSTUFF+=( "$output" ) |
# Let's reorder the stamps, this away user-added stamps get their correct |
# position also remove duplicates. Note AFAIK sort only sorts lines, that's |
# why y replace spaces by newlines. |
# |
# Note that stamps keeps being an array and stamps[1..N] still hold their |
# old values, although it's treated as a string after this |
stamps=$( sed 's/ /\n/g' <<<"${stamps[*]}" | sort -n | uniq ) |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
local NUMSTAMPS=$(wc -w <<<"$stamps") |
TEMPSTUFF+=( $VIDCAPFILE ) |
# TODO: Aspect ratio |
for stamp in $stamps; do |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
if [ $stamp -gt $numsecs ]; then continue; fi |
echo "Generating capture #${n}/${NUMSTAMPS}..." >&2 |
if [ $decoder -eq $DEC_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
p=$(pad 6 $stamp).png |
# mv 00000001.png $dir/$p |
n=$(( $n + 1 )) |
cap=$dir/$p |
# Add the timestamp to each vidcap, doing it hear is much powerful/simple |
# than with the next montage command |
convert -box '#000000aa' \ |
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
$VIDCAPFILE "$cap" |
montage_command+=" $cap" |
done |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
montage_command+=" -geometry x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
fi |
montage_command+=" $output" |
echo "Composing contact sheet..." >&2 |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
mv "$output" . 2>/dev/null || true |
# Codec "prettyfication" |
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
avc1) vcodec="MPEG-4 AVC" ;; |
wmv2) vcodec="WMV 8" ;; # WMV2 is v8 |
esac |
if [ "$vdec" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "$vdec" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in |
85) acodec='MPEG-1 Layer III (MP3)' ;; |
80) acodec='MPEG-1 Layer II (MP2)' ;; |
mp4a) acodec='MPEG-4 AAC' ;; |
353) acodec='WMA 2' ;; |
"") acodec="no audio" ;; |
esac |
local meta="Filename: $f |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "$numsecs")" |
local meta2="Dimensions: ${width}x${height} |
Format: $vcodec / $acodec |
FPS: $fps" |
local signature="$user_signature $user |
$PROGRAM_SIGNATURE" |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $font_heading -pointsize $pts_meta \ |
-background $bg_heading -fill $fg_heading -splice 0x$(( $pts_meta * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $font_sign -pointsize $pts_sign \ |
-background $bg_sign -splice 0x34+0-0 \ |
-fill $fg_sign -draw "text 10,3 '$signature'" "$output" "$output" |
if [ $output_format != "png" ]; then |
local newout="$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
echo -n "Output wrote to " >&2 |
safe_rename "$output" "$(basename "$f").$output_format" |
cleanup |
} |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. An optional unit can be |
used (case-insensitive) , e.g.: |
Seconds: 90 |
Minutes: 3m |
Hours: 1h |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-S|--stamp <arg> Add the image found at the timestamp "arg", same format |
as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-j|--jpeg Output in jpeg (by default output is in png). |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
EOF |
} |
# }}} # Core functionality |
#### Execution starts here #### |
# Execute exithdlr on exit |
trap exithdlr EXIT |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
load_config |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# FIXME: use username / no name at all with -u noarg, and not -u |
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c:m \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:,manual" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a number (given $2)" |
exit $EX_USAGE |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of caps must be a number! (given $2)" |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid interval" |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid interval" |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length" |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid interval" |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a number (given $2)" |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a number (given $2)" |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.2b |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
/ATTIC/video-contact-sheet/tags/1.0.3b/vcs |
---|
0,0 → 1,782 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
declare -r VERSION="1.0.3b" |
# |
# History: |
# |
# 1.0.3b: (2007-04-14) |
# * BUGFIX: Don't put the full video path in the heading |
# |
# 1.0.2b: (2007-04-14) |
# * Licensed under LGPL (was unlicensed before) |
# * Renamed variables and constants to me more congruent |
# * Added DEFAULT_COLS |
# * BUGFIX: Fixed program signature |
# * Streamlined error codes |
# * Added cleanup on failure and on delayed cleanup on success |
# * Changed default signature background to SlateGray (blue-ish gray) |
# |
# 1.0.1a: (2007-04-13) |
# * Print output filename |
# * Added manual mode (all timestamps provided by user) |
# * More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
# * BUGFIX: Discard repeated timestamps |
# * Added "set -e". TODO: Add more verbose error messages when called |
# programs fail. |
# * Added basic support for a user configuration file. |
# |
# 1.0a: (2007-04-10) |
# * First release keeping track of history |
# * Put vcs' url in the signature |
# * Use system username in signature |
# * Added --shoehorn (you get the idea, right?) to feed extra commands to |
# the cappers. Lowelevel and not intended to be used anyway :P |
# * When just a vidcap is requested, take it from the middle of the video |
# * Added -H|--height |
# |
# 0.99.1a: Interim version, renamed to 1.0a |
# |
# 0.99a: |
# * Added shadows |
# * More colourful headers |
# * Easier change of colours/fonts |
# |
# 0.5a: * First usable version |
# 0.1: * First proof of concept |
set -e |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Constants {{{ |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# }}} # End of constants |
# Override-able variables {{{ |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps behind the thumbnails |
declare font_heading=helvetica # Used for meta info box |
declare font_sign=$font_heading # Used for the signature box |
# Font sizes, in points |
declare pts_tstamps=18 # Used for the timestamps |
declare pts_meta=16 # Used for the meta info box |
declare pts_sign=11 # Used for the signature |
# See --shoehorn |
declare shoehorned= |
# }}} # End of override-able variables |
# Options and other internal usage variables, no need to mess with this! |
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare -a TEMPSTUFF=( ) # Temporal files |
# Exit codes, same codes as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
load_config() { |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
) |
if [ ! -f "$CFGFILE" ]; then return 0 ; fi |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
while read line ; do # auto variable $line |
# Don't allow ';', FIXME: dunno how secure that really is |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$line" ; then |
continue |
fi |
if ! egrep -q "^($compregex)=" <<<"$line" ; then |
continue |
fi |
# FIXME: Only print in verbose mose |
# FIXME: Only for really overridden ones |
echo "Overridden variable" $(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<$line) |
eval $line |
done <$CFGFILE |
} |
# {{{ # Convenience functions |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
return $? |
} |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
# Only allowed characters |
if ! grep -q '[0-9smh]' <<<"$s"; then |
return $EX_USAGE; |
fi |
# FIXME: Find some cleaner way |
local i= c= num= sum=0 |
for i in $(seq 0 $(( ${#s} - 1)) ); do |
c=${s:$i:1} |
if is_number $c ; then |
num+=$c |
else |
case $c in |
h) num=$(($num * 3600)) ;; |
m) num=$(($num * 60)) ;; |
s) ;; |
*) |
return $EX_SOFTWARE |
;; |
esac |
sum=$(($sum + $num)) |
num= |
fi |
done |
# If last element was a number, it's seconds and they weren't added |
if is_number $c ; then |
sum=$(( $sum + $num )) |
fi |
echo $sum |
return 0 |
} |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
pretty_stamp() { |
if ! is_number "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with -1, -2, ... |
safe_rename() { |
local from="$1" |
local to="$2" |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
local n=1 |
while [ -f "$to" ]; do |
to="$(basename "$2" .$ext)-$n.$ext" |
n=$(( $n + 1)) |
done |
mv "$from" "$to" |
echo "$to" >&2 |
} |
test_programs() { |
for prog in mplayer convert montage ffmpeg ; do |
type -pf "$prog" >/dev/null |
local retval=$? |
if [ $retval -ne 0 ] ; then |
error "Required program $prog not found!" |
return $retval |
fi |
done |
} |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
echo "Cleaning up..." >&2 # TODO: Only in verbose mode |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
exithdlr() { |
cleanup |
} |
# Print some text to stderr |
error() { |
echo "$1" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
echo "Processing $f..." >&2 |
# Meta data extraction |
# Don't change the -vc as it would affect $vdec |
local mplayer_cache=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) # FourCC |
local acodec=$(grep ID_AUDIO_FORMAT <<<"$mplayer_cache" | cut -d'=' -f2) |
local vdec=$(grep ID_VIDEO_CODEC <<<"$mplayer_cache" | cut -d'=' -f2) # Decoder |
local width=$(grep ID_VIDEO_WIDTH <<<"$mplayer_cache" | cut -d'=' -f2) |
local height=$(grep ID_VIDEO_HEIGHT <<<"$mplayer_cache" | cut -d'=' -f2) |
local fps=$(grep ID_VIDEO_FPS <<<"$mplayer_cache" | cut -d'=' -f2) |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=$height |
fi |
local numsecs=$(grep ID_LENGTH <<<"$mplayer_cache"| cut -d'=' -f2 | cut -d. -f1) |
if ! is_number $numsecs ; then |
error "Internal error!" |
return $EX_SOFTWARE |
fi |
local nc=$numcaps |
local in=$interval |
# Start bound: |
local startsec=0 |
if [ $startsec -lt $fromtime ]; then |
startsec=$fromtime |
fi |
# End bound: |
local endsec=$numsecs |
if [ $totime -ne -1 ] && [ $endsec -gt $totime ]; then |
endsec=$totime |
fi |
if [ $startsec -ne 0 ] || [ $endsec -ne $numsecs ]; then |
echo "Restricting to range [$startsec..$endsec]" >&2 |
fi |
local delta=$(( $endsec - $startsec )) |
# FIXME: the total # of caps is currently broken when using -f and -t |
# Note that when numcaps mandates the interval is obtained from |
# the actually allowed secods (hence it is not movie_length / numcaps ) |
# Adjust interval/numcaps: |
if [ "$timecode_from" -eq "$TC_INTERVAL" ]; then |
# Interval rules => it doesn't change |
nc=$(( $delta / $interval )) |
# If a multiple, an extra vidcap is generated (at the last second) |
if [ $(( $delta % $interval )) -eq 0 ]; then |
nc=$(( $nc + 1 )) |
fi |
elif [ "$timecode_from" -eq "$TC_NUMCAPS" ]; then |
# Numcaps rules => it doesn't change |
if [ $numcaps -eq 1 ]; then # If just one cap, center it |
in=$(( $numsecs / 2 )) |
else |
in=$(( $numsecs / $numcaps )) |
fi |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi |
# Let's try to make some sense... |
# Minimum interval allowance: |
if [ $in -gt $numsecs ]; then |
error "The interval is longer than the video length." |
error "Use a lower interval or numcaps instead." |
error "Skipping \"$f\"." |
return $EX_USAGE |
fi |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
# Tempdir |
local dir=$(mktemp -d -p . vcs.XXXXXX) |
if [ "$?" -ne 0 ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$dir" ) |
local n= |
# Get the stamps (if in auto mode)... |
local stamps=( ) |
if [ $manual_mode -ne 1 ]; then |
n=$(( $startsec + $in )) |
stamps=( ${initial_stamps[*]} $n ) |
while [ $n -le $endsec ]; do |
n=$(( $n + $in )) |
if [ $n -gt $endsec ]; then break; fi |
stamps=( ${stamps[*]} $n ) |
done |
else |
stamps=( ${initial_stamps[*]} ) |
fi |
n=1 |
local p="" |
local cap="" |
local montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png' -d .) |
TEMPSTUFF+=( "$output" ) |
# Let's reorder the stamps, this away user-added stamps get their correct |
# position also remove duplicates. Note AFAIK sort only sorts lines, that's |
# why y replace spaces by newlines. |
# |
# Note that stamps keeps being an array and stamps[1..N] still hold their |
# old values, although it's treated as a string after this |
stamps=$( sed 's/ /\n/g' <<<"${stamps[*]}" | sort -n | uniq ) |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
local NUMSTAMPS=$(wc -w <<<"$stamps") |
TEMPSTUFF+=( $VIDCAPFILE ) |
# TODO: Aspect ratio |
for stamp in $stamps; do |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
if [ $stamp -gt $numsecs ]; then continue; fi |
echo "Generating capture #${n}/${NUMSTAMPS}..." >&2 |
if [ $decoder -eq $DEC_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
p=$(pad 6 $stamp).png |
# mv 00000001.png $dir/$p |
n=$(( $n + 1 )) |
cap=$dir/$p |
# Add the timestamp to each vidcap, doing it hear is much powerful/simple |
# than with the next montage command |
convert -box '#000000aa' \ |
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
$VIDCAPFILE "$cap" |
montage_command+=" $cap" |
done |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
montage_command+=" -geometry x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
fi |
montage_command+=" $output" |
echo "Composing contact sheet..." >&2 |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
mv "$output" . 2>/dev/null || true |
# Codec "prettyfication" |
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
avc1) vcodec="MPEG-4 AVC" ;; |
wmv2) vcodec="WMV 8" ;; # WMV2 is v8 |
esac |
if [ "$vdec" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "$vdec" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in |
85) acodec='MPEG-1 Layer III (MP3)' ;; |
80) acodec='MPEG-1 Layer II (MP2)' ;; |
mp4a) acodec='MPEG-4 AAC' ;; |
353) acodec='WMA 2' ;; |
"") acodec="no audio" ;; |
esac |
local meta="Filename: $(basename "$f") |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "$numsecs")" |
local meta2="Dimensions: ${width}x${height} |
Format: $vcodec / $acodec |
FPS: $fps" |
local signature="$user_signature $user |
$PROGRAM_SIGNATURE" |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $font_heading -pointsize $pts_meta \ |
-background $bg_heading -fill $fg_heading -splice 0x$(( $pts_meta * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $font_sign -pointsize $pts_sign \ |
-background $bg_sign -splice 0x34+0-0 \ |
-fill $fg_sign -draw "text 10,3 '$signature'" "$output" "$output" |
if [ $output_format != "png" ]; then |
local newout="$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
echo -n "Output wrote to " >&2 |
safe_rename "$output" "$(basename "$f").$output_format" |
cleanup |
} |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. An optional unit can be |
used (case-insensitive) , e.g.: |
Seconds: 90 |
Minutes: 3m |
Hours: 1h |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-S|--stamp <arg> Add the image found at the timestamp "arg", same format |
as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-j|--jpeg Output in jpeg (by default output is in png). |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
EOF |
} |
# }}} # Core functionality |
#### Execution starts here #### |
# Execute exithdlr on exit |
trap exithdlr EXIT |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
load_config |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# FIXME: use username / no name at all with -u noarg, and not -u |
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c:m \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:,manual" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a number (given $2)" |
exit $EX_USAGE |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of caps must be a number! (given $2)" |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid interval" |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid interval" |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length" |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid interval" |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a number (given $2)" |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a number (given $2)" |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.3b |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.4b/CHANGELOG |
---|
0,0 → 1,58 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.4b/vcs |
---|
0,0 → 1,839 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
declare -r VERSION="1.0.4b" |
# |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# 1.0.4b: (2007-04-17) |
# * Added error checks for failures to create vidcap or to process it |
# convert |
# * BUGFIX: Corrected error check on tempdir creation |
# * BUGFIX: Use temporary locations for temporary files (thanks to |
# Alon Levy). |
# * Aspect ratio support (might be buggy). Requires bc. |
# * Added $safe_rename_pattern to allow overriding the default alternate |
# naming when the output file exists |
# * Moved previous previous versions' changes to a separate file. |
# * Support for per-dir and system-wide configuration files. Precedence |
# in ascending order: |
# /etc/vcs.conf ~/.vcs.conf ./vcs.conf |
# * Added default_options (broken, currently ignored) |
# * BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
# * Added codec ids of WMV9 and WMA3 |
# |
set -e |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Constants {{{ |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# }}} # End of constants |
# Override-able variables {{{ |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps behind the thumbnails |
declare font_heading=helvetica # Used for meta info box |
declare font_sign=$font_heading # Used for the signature box |
# Font sizes, in points |
declare pts_tstamps=18 # Used for the timestamps |
declare pts_meta=16 # Used for the meta info box |
declare pts_sign=11 # Used for the signature |
# See --shoehorn |
declare shoehorned= |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
declare default_options= |
# }}} # End of override-able variables |
# Options and other internal usage variables, no need to mess with this! |
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
declare -a TEMPSTUFF=( ) # Temporal files |
# Exit codes, same codes as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
load_config() { |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
'default_options' |
) |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
local basecfg="$(basename "$CFGFILE")" |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
# Don't allow ';', FIXME: dunno how secure that really is |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$line" ; then |
continue |
fi |
if ! egrep -q "^($compregex)=" <<<"$line" ; then |
continue |
fi |
# FIXME: Only print in verbose mose |
# FIXME: Only for really overridden ones |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<$line) |
echo "Overridden variable $varname from file $cfgfile" |
eval $line |
done <$cfgfile |
done |
} |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction |
# Only accepts XX/YY |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width(height) (=AR*height) (rounded) |
compute_width() { |
local wfloat=$(bc -lq <<< "$aspect_ratio * $1") |
local wint=$(bc -q <<<"($wfloat+0.5)/1") |
echo $wint |
} |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
# Only allowed characters |
if ! grep -q '[0-9smh]' <<<"$s"; then |
return $EX_USAGE; |
fi |
# FIXME: Find some cleaner way |
local i= c= num= sum=0 |
for i in $(seq 0 $(( ${#s} - 1)) ); do |
c=${s:$i:1} |
if is_number $c ; then |
num+=$c |
else |
case $c in |
h) num=$(($num * 3600)) ;; |
m) num=$(($num * 60)) ;; |
s) ;; |
*) |
return $EX_SOFTWARE |
;; |
esac |
sum=$(($sum + $num)) |
num= |
fi |
done |
# If last element was a number, it's seconds and they weren't added |
if is_number $c ; then |
sum=$(( $sum + $num )) |
fi |
echo $sum |
return 0 |
} |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
pretty_stamp() { |
if ! is_number "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stderr |
safe_rename() { |
local from="$1" |
local to="$2" |
# Extension |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
# Basename without extension |
local b=$(basename "$2" ".$ext") |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s/%b/$b/g" <<<"$safe_rename_pattern") |
to=$(sed "s/%N/$n/g" <<<"$to") |
to=$(sed "s/%e/$ext/g" <<<"$to") |
let 'n++'; |
done |
mv "$from" "$to" |
echo "$to" >&2 |
} |
test_programs() { |
for prog in mplayer convert montage ffmpeg bc ; do |
type -pf "$prog" >/dev/null |
local retval=$? |
if [ $retval -ne 0 ] ; then |
error "Required program $prog not found!" |
return $retval |
fi |
done |
} |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
echo "Cleaning up..." >&2 # TODO: Only in verbose mode |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
exithdlr() { |
cleanup |
} |
# Print some text to stderr |
error() { |
echo "$1" >&2 |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
MPLAYER_CACHE= |
numsecs() { |
echo $(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
} |
compute_timecodes() { |
local st=0 numsecs=$(numsecs) end= |
end=$numsecs |
if [ $st -lt $fromtime ]; then |
st=$fromtime |
fi |
if [ $totime -gt 0 ] && [ $end -gt $totime ]; then |
end=$totime |
fi |
local inc= |
if [ "$timecode_from" -eq $TC_INTERVAL ]; then |
inc=$interval |
elif [ "$timecode_from" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $numcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(( ($end-$st) / 2 + 1)) |
else |
inc=$(( ($end-$st) / $numcaps )) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [ $inc -gt $numsecs ]; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp= |
for stamp in $(seq $st $inc $end); do |
LTC+=( $stamp ) |
done |
unset LTC[0] # Initial cap (=$st) |
TIMECODES=( ${TIMECODES[*]} ${LTC[*]} ) |
} |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
echo "Processing $f..." >&2 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
local vcodec=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
local acodec=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
local vdec=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder |
local width=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
local height=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
local fps=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=$height |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "$width / $height") |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
if ! is_number $numsecs ; then |
error "Internal error!" |
return $EX_SOFTWARE |
fi |
local nc=$numcaps |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
# Tempdir |
local dir=$(mktemp -d -t vcs.XXXXXX) |
if [ ! -d "$dir" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$dir" ) |
local n= |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes |
fi |
n=1 |
local p="" |
local montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(tempfile --prefix "vcs-" --suffix '-preview.png') |
TEMPSTUFF+=( "$output" ) |
# Let's reorder the stamps, this away user-added stamps get their correct |
# position also remove duplicates. Note AFAIK sort only sorts lines, that's |
# why y replace spaces by newlines. |
local stamps=$( sed 's/ /\n/g' <<<"${TIMECODES[*]}" | sort -n | uniq ) |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
local NUMSTAMPS=$(wc -w <<<"$stamps") |
TEMPSTUFF+=( $VIDCAPFILE ) |
local capfile= |
# TODO: Aspect ratio |
for stamp in $stamps; do |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
if [ $stamp -gt $numsecs ]; then continue; fi |
echo "Generating capture #${n}/${NUMSTAMPS}..." >&2 |
p=$(pad 6 $stamp).png |
capfile=$dir/$p |
if [ $decoder -eq $DEC_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
let 'n++' # $n++ |
# Add the timestamp to each vidcap, doing it here is much powerful/simple |
# than with the next montage command |
# Note the '!', it is necessary to apply aspect ratio change |
convert -box '#000000aa' -geometry ${vidcap_width}x${vidcap_height}! \ |
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
$VIDCAPFILE "$capfile" |
if [ ! -f "$capfile" ]; then |
error "Failed to process capture" |
return $EX_CANTCREAT |
fi |
montage_command+=" \"$capfile\"" |
done |
unset capfile |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
montage_command+=" -geometry ${vidcap_width}x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
fi |
montage_command+=" \"$output\"" |
echo "Composing contact sheet..." >&2 |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
# Codec "prettyfication" |
case $( tr '[A-Z]' '[a-z]' <<<$vcodec) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
avc1) vcodec="MPEG-4 AVC" ;; |
wmv2) vcodec="WMV8" ;; # v2 is same as v8 |
wmv3) vcodec="WMV9" ;; |
esac |
if [ "$vdec" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "$vdec" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<$acodec ) in |
85) acodec='MPEG-1 Layer III (MP3)' ;; |
80) acodec='MPEG-1 Layer II (MP2)' ;; |
mp4a) acodec='MPEG-4 AAC' ;; |
353) acodec='WMA2' ;; |
354) acodec='WMA3' ;; |
"") acodec="no audio" ;; |
esac |
local meta="Filename: $(basename "$f") |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "$numsecs")" |
local meta2="Dimensions: ${width}x${height} |
Format: $vcodec / $acodec |
FPS: $fps" |
local signature="$user_signature $user |
$PROGRAM_SIGNATURE" |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $font_heading -pointsize $pts_meta \ |
-background $bg_heading -fill $fg_heading -splice 0x$(( $pts_meta * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $font_sign -pointsize $pts_sign \ |
-background $bg_sign -splice 0x34+0-0 \ |
-fill $fg_sign -draw "text 10,3 '$signature'" "$output" "$output" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
echo -n "Output wrote to " >&2 |
safe_rename "$output" "$(basename "$f").$output_format" |
cleanup |
} |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. An optional unit can be |
used (case-insensitive) , e.g.: |
Seconds: 90 |
Minutes: 3m |
Hours: 1h |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-S|--stamp <arg> Add the image found at the timestamp "arg", same format |
as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or fractions. |
-j|--jpeg Output in jpeg (by default output is in png). |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You probably don't need it. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
EOF |
} |
# }}} # Core functionality |
#### Execution starts here #### |
# Execute exithdlr on exit |
trap exithdlr EXIT |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
load_config |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# TODO: use no name at all with -u noarg |
#eval set -- "${default_options} ${@}" |
TEMP=$(getopt -o i:n:u:T:f:t:S:jhFMH:c:ma: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a number (given $2)" |
exit $EX_USAGE |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of caps must be a number! (given $2)" |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid interval" |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid interval" |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length" |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid interval" |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a number (given $2)" |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a floating point "\ |
"number or as a fraction (ie: 1, 1.33, 4/3, 2.5)" |
exit 12 |
fi |
aspect_ratio="$2" |
shift |
;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a number (given $2)" |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.4b |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.5b/CHANGELOG |
---|
0,0 → 1,88 |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.5b/vcs |
---|
0,0 → 1,1320 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
declare -r VERSION="1.0.5b" |
# |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# TODO: Support for ms timestamps (ffmpeg supports it e.g. 54.9 is ok and != 54) |
# |
# 1.0.5b: (2007-04-20) |
# * INTERNAL: Split functionality in more separate pieces (functions) |
# * BUGFIX: Corrected --aspect declaration |
# * CLEANUP: Put all temporary files in the same temporary directory |
# * FEATURE: Highlight support |
# * FEATURE: Extended mode (-e) |
# * FEATURE: Added -U (--fullname) |
# * Requirements detection now prints all failed requirements |
# * BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
# than video |
# * Don't print the sucess line unless it was really successful |
# * Allow quiet operation (-q and -qq), and different verbosity levels |
# (only through config overrides) |
# * Print vcs' identification on operation |
# * FEATURE: Auto aspect ratio (-A, --autoaspect) |
# * INTERNAL: Added better documentation of functions |
# * Print coloured messages if possible (can be disabled by overriding |
# $plain_messages) |
# * FEATURE: Command line overrides (-O, --override) |
# * BUGFIX: Don't allow setting -n0 |
# * Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
# * Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
# from mplayer's identification at least, between MPEG-1 and MPEG-2 |
# * Audio identified as MP2 can also actually be MP1, added it to the codec id |
# * Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
# OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
# Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
# MS Video 1 and MS RLE) |
# * Print the number of channels if != 2 |
# |
set -e |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Constants {{{ |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# }}} # End of constants |
# Override-able variables {{{ |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps behind the thumbnails |
declare font_heading=helvetica # Used for meta info box |
declare font_sign=$font_heading # Used for the signature box |
# Font sizes, in points |
declare pts_tstamps=18 # Used for the timestamps |
declare pts_meta=16 # Used for the meta info box |
declare pts_sign=11 # Used for the signature |
# See --shoehorn |
declare shoehorned= |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# }}} # End of override-able variables |
# Options and other internal usage variables, no need to mess with this! |
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF=( ) # Temporal files |
declare -a TIMECODES=( ) # Timestamps of the video captures |
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE... |
declare -a VID= |
# ...and these are the indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same codes as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
) |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
bc -q <<<"( $f + 0.5 ) / 1" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
# Only allowed characters |
if ! grep -q '[0-9smh]' <<<"$s"; then |
return $EX_USAGE; |
fi |
# FIXME: Find some cleaner way |
local i= c= num= sum=0 |
for i in $(seq 0 $(( ${#s} - 1)) ); do |
c=${s:$i:1} |
if is_number $c ; then |
num+=$c |
else |
case $c in |
h) num=$(($num * 3600)) ;; |
m) num=$(($num * 60)) ;; |
s) ;; |
*) |
return $EX_SOFTWARE |
;; |
esac |
sum=$(($sum + $num)) |
num= |
fi |
done |
# If last element was a number, it's seconds and they weren't added |
if is_number $c ; then |
sum=$(( $sum + $num )) |
fi |
echo $sum |
return 0 |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_number "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
safe_rename() { |
local from="$1" |
local to="$2" |
# Extension |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
# Basename without extension |
local b=$(basename "$2" ".$ext") |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s/%b/$b/g" <<<"$safe_rename_pattern") |
to=$(sed "s/%N/$n/g" <<<"$to") |
to=$(sed "s/%e/$ext/g" <<<"$to") |
let 'n++'; |
done |
mv "$from" "$to" |
echo "$to" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in mplayer convert montage bc ffmpeg ; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
info "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" >&2 ; tput sgr0 |
fi |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" >&2 ; tput sgr0 |
fi |
} |
# |
# Print an informational message |
# info($1 = text) |
info() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" >&2 ; tput sgr0 |
fi |
} |
# |
# Same as info but with no colour ever. |
# infoplain($1 = text) |
infoplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
local r=$(tempfile -d "$VCSTEMPDIR" -p "vcs-" -s "$1") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 |
# globals: fromtime, totime, timecode_from, TIMECODES |
if [ $st -lt $fromtime ]; then |
st=$fromtime |
fi |
if [ $totime -gt 0 ] && [ $end -gt $totime ]; then |
end=$totime |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(( ($end-$st) / 2 + 1)) |
else |
inc=$(( ($end-$st) / $tcnumcaps )) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [ $inc -gt ${VID[$LEN]} ]; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp= |
for stamp in $(seq $st $inc $end); do |
LTC+=( $stamp ) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[*]} ${LTC[*]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect() |
guess_aspect() { |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=${VID[$W]} h=${VID[$H]} |
if [ $w -eq 352 ]; then # VCD / DVD @ VCD Res. / Half-D1 / CVD |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
aspect_ratio=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then # Half-D1 / CVD |
aspect_ratio=4/3 |
fi |
elif [ $w -eq 704 ] || [ $w -eq 720 ]; then # DVD / DVB |
# Actually for 720x576/720x480 16/9 is as good a guess |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
aspect_ratio=4/3 |
fi |
elif [ $w -eq 480 ]; then # SVCD |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
aspect_ratio=4/3 |
fi |
else |
warn "Couldn't guess aspect ratio." |
aspect_ratio=$(bc -lq <<<"$w / $h") |
fi |
local AR=$(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio) |
info "Aspect ratio set to $AR" |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000001.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: For some reason -ss before -i failed on my mkv sample |
# while after -i it failed on my wmv9 sample ¿? |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Draw a timestamp in the file |
# apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
apply_stamp() { |
local filename=$1 timestamp=$2 width=$3 height=$4 |
local temp=$(new_temp_file ".png") |
mv "$filename" "$temp" |
# Add the timestamp to each vidcap, doing it here is much powerful/simple |
# than with the next montage command |
# Note the '!', it is necessary to apply aspect ratio change |
convert -box '#000000aa' -geometry ${width}x${height}! \ |
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
"$temp" "$filename" |
if [ ! -f "$filename" ]; then |
error "Failed to add timestamp to capture" |
mv "$temp" "$filename" # Leave things as before |
return $EX_CANTCREAT |
fi |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_number "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
info "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
guess_aspect |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
create_temp_dir |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlighs |
local hlfile="$VCSTEMPDIR/highlights.png" n=1 # Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlmontage_command="montage -gravity SouthEast -texture xc:LightGoldenRod " |
local hlcapfile= |
local pretty= |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if [ $stamp -gt $numsecs ]; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
info "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
apply_stamp "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
hlmontage_command+=" \"$hlcapfile\"" |
let 'n++' |
done |
#if [ "$title" ]; then |
# hlmontage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
#fi |
info "Composing highlights contact sheet..." |
eval "$hlmontage_command -geometry ${vidcap_width}x${vidcap_height}!+10+5 \ |
-tile ${numcols}x -shadow \"$hlfile\"" |
unset hlcapfile hlmontage_command pretty |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile= pretty= n=1 montage_command=$base_montage_command |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
# This shouldn't occur automatically anymore with the new code. |
if [ $stamp -gt $numsecs ]; then let 'n++' && continue; fi |
info "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
apply_stamp "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
# move to tempdir/<frame num>.png, cap num is padded to 6 characters |
mv "$VIDCAPFILE" "$capfile" |
montage_command+=" \"$capfile\"" |
let 'n++' # $n++ |
done |
unset capfile pretty n |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
# FIXME: Title should go before the highlights |
montage_command+=" -geometry ${vidcap_width}x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
fi |
montage_command+=" \"$output\"" |
info "Composing standard contact sheet..." |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
unset montage_command |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of 2 |
# TODO: Round it to a multiplier of the number of columns |
local hlnc=$(bc -q <<<"( (${#TIMECODES[*]} * $extended_factor) / 2 * 2)") |
unset TIMECODES # required step to get the right count |
TIMECODES=${initial_stamps[*]} |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= montage_command=$base_montage_command |
extoutput=$(new_temp_file "-extended.png") |
# The image size of the extra captures is 1/4 |
let 'w=vidcap_width/2, h=vidcap_height/2' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
info "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
apply_stamp "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
montage_command+=" \"$capfile\"" |
let 'n++' |
done |
montage_command+=" -geometry ${w}x${h}+5+2 -tile $(($numcols * 2))x -shadow" |
info "Composing extended contact sheet..." |
eval $montage_command "$extoutput" |
unset montage_command w h capfile pretty n |
fi |
# Codec "prettyfication" |
# Official FourCCs: <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# Unofficial list: <http://www.fourcc.org/> |
# Another software with a list of fourccs -> name mappings: |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php?view=markup> |
local vcodec= acodec= |
case $( tr '[A-Z]' '[a-z]' <<<${VID[$VCODEC]}) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
wmv1) vcodec="WMV7" ;; |
wmv2) vcodec="WMV8" ;; |
wmv3) vcodec="WMV9" ;; |
fmp4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
mp42) vcodec="MS MPEG-4 v2" ;; |
mpg4) vcodec="MS MPEG-4 v1" ;; |
mp43) vcodec="MS MPEG-4 v3" ;; |
div3) vcodec="DivX ;) Low Motion" ;; # Technically same as mp43 |
i420) vcodec="Raw I420" ;; # XXX: 420 presumably stands by 4:2:0 ? |
rv10) vcodec="RealVideo 1.0/5.0" ;; |
rv20) vcodec="RealVideo G2" ;; |
svq1) vcodec="Sorenson Video 1" ;; |
svq3) vcodec="Sorenson Video 3" ;; |
# These are known FourCCs that I haven't tested against so far |
wmva) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported. |
rv30) vcodec="RealVideo 8" ;; |
rv40) vcodec="RealVideo 9/10" ;; |
div4) vcodec="DivX ;) Fast Motion" ;; |
divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) |
iv50) vcodec="Indeo 5.0" ;; |
mjpg) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
iv32) vcodec="Indeo 3.2" ;; |
msvc) vcodec="Microsoft Video 1" ;; |
mrle) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
local meta="Filename: $(basename "$f") |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "${VID[$LEN]}")" |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature="$user_signature $user |
$PROGRAM_SIGNATURE" |
unset acodec vcodec |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
info "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( "$hlfile" -background LightGoldenRod \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
info "Creating header and footer..." |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $font_heading -pointsize $pts_meta \ |
-background $bg_heading -fill $fg_heading -splice 0x$(( $pts_meta * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $font_sign -pointsize $pts_sign \ |
-background $bg_sign -splice 0x34+0-0 \ |
-fill $fg_sign -draw "text 10,3 '$signature'" "$output" "$output" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
info "Done. Output wrote to $output_name" |
cleanup |
} |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
info "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-e[arg] | --extended=[arg] |
Enables extended mode and optionally sets the extended |
factor. By default it's $DEFAULT_EXT_FACTOR. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or |
fractions. |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode: Currently just prints the parsed |
commandline as the title and to stderr. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Core functionality |
#### Execution starts here #### |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# TODO: use no name at all with -u noarg |
#eval set -- "${default_options} ${@}" |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::UqAO: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,quiet,autoaspect,override:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-D) echo "Command line: $0 $*" && title="$0 $*" ; ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.5b |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/tags/1.0.6b/CHANGELOG |
---|
0,0 → 1,92 |
1.0.6b: (2007-04-21) (Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the sucess line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.6b/vcs |
---|
0,0 → 1,1298 |
#!/bin/bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007 Toni Corvera |
# |
# This library is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This library is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@outlyer.net> |
# |
declare -r VERSION="1.0.6b" |
# |
# History (The full changelog was moved to a separate file and can be found |
# at <http://p.outlyer.net/vcs/files/CHANGELOG>). |
# |
# TODO: Support for ms timestamps (ffmpeg supports it e.g. 54.9 is ok and != 54) |
# |
# 1.0.6b: (2007-04-21) (Bugfix release) |
# * BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
# * Make sure mktemp is installed, just in case ;) |
# |
set -e |
# Configuration file, please, use this file to modify the behaviour of the |
# script. Using this allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of three configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * $CFGFILE (by default ~/.vcs.conf): Per-user conf, second least precedence |
# * ./vcs.conf: Per-dir confif, most precedence |
# |
# The variables that can be overriden are below the block of constants ahead. |
declare -r CFGFILE=~/.vcs.conf |
# Constants {{{ |
# see $decoder |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $timecode_from |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="with Video Contact Sheet *NIX ${VERSION} <http://p.outlyer.net/vcs/>" |
# see $safe_rename_pattern |
declare -r DEFAULT_SAFE_REN_PATT="%b-%N.%e" |
# see $extended_factor |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $verbosity |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# }}} # End of constants |
# Override-able variables {{{ |
declare -i DEFAULT_INTERVAL=300 |
declare -i DEFAULT_NUMCAPS=16 |
declare -i DEFAULT_COLS=2 |
# Text before the user name in the signature |
declare user_signature="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare user=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i timecode_from=$TC_INTERVAL |
# Which of the two vidcappers should be used (see -F, -M) |
# mplayer seems to fail for mpeg or WMV9 files, at least on my system |
# also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
# seeking while mplayer apparently only seeks to nearest keyframe |
declare -i decoder=$DEC_FFMPEG |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare output_format=png # ImageMagick decides the type from the extension |
declare -i output_quality=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare bg_heading=YellowGreen # Background for meta info (size, codec...) |
declare bg_sign=SlateGray # Background for signature |
declare fg_heading=black # Font colour for meta info box |
declare fg_sign=black # Font colour for signature |
declare fg_tstamps=white # Font colour for timestamps |
# Fonts, see convert -list type to get the list |
declare font_tstamps=courier # Used for timestamps behind the thumbnails |
declare font_heading=helvetica # Used for meta info box |
declare font_sign=$font_heading # Used for the signature box |
# Font sizes, in points |
declare pts_tstamps=18 # Used for the timestamps |
declare pts_meta=16 # Used for the meta info box |
declare pts_sign=11 # Used for the signature |
# See --shoehorn |
declare shoehorned= |
# This can only be changed in the configuration file |
# Change it to change the safe renanimg: |
# When writing the output file, the input name + output extension is |
# used (e.g.: "some video.avi.png"), if it already exists, though, |
# a number if appended to the name. This variable dictates where the number is |
# placed. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# The default creates outputs like "output.avi-1.png" |
# |
# If overridden with an incorrect value it will be silently set to the default |
declare safe_rename_pattern="$DEFAULT_SAFE_REN_PATT" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare extended_factor=0 |
# Options added always to the ones in the command line |
# (command line options override them). |
# Note using this is a bit tricky :P mostly because I've no clue of how this |
# should be done. |
# As an example: you want to set always the title to "My Title" and output |
# to jpeg: default_options="-T'My Title' -j" |
declare default_options= |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i verbosity=$V_ALL |
# When set to 0 the status messages printed by vcs while running |
# are coloured if the terminal supports it. Set to 1 if this annoys you. |
declare -i plain_messages=0 |
# }}} # End of override-able variables |
# Options and other internal usage variables, no need to mess with this! |
declare -i interval=$DEFAULT_INTERVAL # Interval of captures (=numsecs/numcaps) |
declare -i numcaps=$DEFAULT_NUMCAPS # Number of captures (=numsecs/interval) |
declare title="" |
declare -i fromtime=0 # Starting second (see -f) |
declare -i totime=-1 # Ending second (see -t) |
declare -a initial_stamps=( ) # Manually added stamps (see -S) |
declare -i th_height= # Height of the thumbnails, by default use same as input |
declare -i cols=$DEFAULT_COLS # Number of output columns |
declare -i manual_mode=0 # if 1, only command line timestamps will be used |
declare aspect_ratio=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF=( ) # Temporal files |
declare -a TIMECODES=( ) # Timestamps of the video captures |
declare -a HLTIMECODES=( ) # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporal directory, all temporal files |
# go there |
# This holds the output of mplayer -identify on the current video |
declare MPLAYER_CACHE= |
# This holds the parsed values of MPLAYER_CACHE... |
declare -a VID= |
# ...and these are the indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 |
# Exit codes, same codes as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# These are the variables allowed to be overriden in the config file, |
# please. |
# They're REGEXes, they'll be concatenated to form a regex like |
# (override1|override2|...). |
# Don't mess with this unless you're pretty sure of what you're doing. |
# All this extra complexity is done to avoid including the config |
# file directly for security reasons. |
declare -ra ALLOWED_OVERRIDES=( |
'user' |
'user_signature' |
'bg_.*' |
'font_.*' |
'pts_.*' |
'fg_.*' |
'output_quality' |
'DEFAULT_INTERVAL' |
'DEFAULT_NUMCAPS' |
'DEFAULT_COLS' |
'decoder' |
'output_format' |
'shoehorned' |
'timecode_from' |
'safe_rename_pattern' |
'default_options' |
'extended_factor' |
'verbosity' |
'plain_messages' |
) |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local CONFIGS=( /etc/vcs.conf $CFGFILE ./vcs.conf) |
for cfgfile in ${CONFIGS[*]} ;do |
if [ ! -f "$cfgfile" ]; then continue; fi |
while read line ; do # auto variable $line |
override "$line" "file $cfgfile" # Feeding it comments should be harmless |
done <$cfgfile |
done |
} |
# Do an override |
# It takes basically an assignment (in the same format as bash) |
# to one of the override-able variables (see $ALLOWED_OVERRIDES). |
# There are some restrictions though. Currently ';' is not allowed to |
# be in the assignment. |
# override($1 = bash variable assignment, $2 = source) |
override() { |
local o="$1" |
local src="$2" |
local compregex=$( sed 's/ /|/g' <<<${ALLOWED_OVERRIDES[*]} ) |
# Don't allow ';', FIXME: dunno how secure that really is... |
# FIXME: ...it doesn't really works anyway |
if ! egrep -q '^[[:space:]]*[a-zA-Z_][a-zA-Z0-9_]*=[^;]*' <<<"$o" ; then |
return |
fi |
if ! egrep -q "^($compregex)=" <<<"$o" ; then |
return |
fi |
local varname=$(sed -r 's/^[[:space:]]*([a-zA-Z0-9_]*)=.*/\1/'<<<"$o") |
local varval=$(sed -r 's/[^=]*=(.*)/\1/'<<<"$o") |
# FIXME: Security! |
local curvarval= |
eval curvarval='$'"$varname" |
if [ "$curvarval" == "$varval" ]; then |
warn "Ignored override '$varname' (already had same value)" |
else |
eval "$varname=\"$varval\"" |
# FIXME: Only for really overridden ones |
warn "Overridden variable '$varname' from $src" |
fi |
} |
# {{{ # Convenience functions |
# Returns true if input is composed only of numbers |
# is_number($1 = input) |
is_number() { |
egrep -q '^[0-9]+$' <<<"$1" |
} |
# Returns true if input can be parsed as a floating point number |
# Accepted: XX.YY XX. .YY (.24=0.24 |
# is_float($1 = input) |
is_float() { |
egrep -q '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$'<<<"$1" |
} |
# Returns true if input is a fraction |
# Only accepts XX/YY |
# is_fraction($1 = input) |
is_fraction() { |
egrep -q '^[0-9]+/[0-9]+$'<<<"$1" |
} |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
local exp=$(sed 's/[ ,]/*/g'<<<"$@") # bc expression |
local f=$(bc -lq<<<"$exp") # exact float value |
# division is integer by default (without -l) so it's the smae |
# as rounding to the lower int |
bc -q <<<"( $f + 0.5 ) / 1" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $aspect_ratio,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
if is_number "$1" ; then echo $1 ; return 0 ; fi |
local s=$(tr '[A-Z]' '[a-z]' <<<"$1") |
# Only allowed characters |
if ! grep -q '[0-9smh]' <<<"$s"; then |
return $EX_USAGE; |
fi |
# FIXME: Find some cleaner way |
local i= c= num= sum=0 |
for i in $(seq 0 $(( ${#s} - 1)) ); do |
c=${s:$i:1} |
if is_number $c ; then |
num+=$c |
else |
case $c in |
h) num=$(($num * 3600)) ;; |
m) num=$(($num * 60)) ;; |
s) ;; |
*) |
return $EX_SOFTWARE |
;; |
esac |
sum=$(($sum + $num)) |
num= |
fi |
done |
# If last element was a number, it's seconds and they weren't added |
if is_number $c ; then |
sum=$(( $sum + $num )) |
fi |
echo $sum |
return 0 |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
local len=$1 |
local str=$2 |
while [ ${#str} -lt $len ]; do |
str=0$str |
done |
echo $str |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
if ! is_number "$1" ; then return $EX_USAGE ; fi |
local t=$1 |
local h=$(( $t / 3600 )) |
t=$(( $t % 3600 )) |
local m=$(( $t / 60 )) |
t=$(( $t % 60 )) |
local s=$t |
local R="" |
if [ $h -gt 0 ]; then |
R+="$h:" |
fi |
R+=$(pad 2 "$m"):$(pad 2 $s) |
echo $R |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_size($1 = file) |
get_pretty_size() { |
local f="$1" |
local bytes=$(du -DL --bytes "$f" | cut -f1) |
local size="" |
if [ "$bytes" -gt $(( 1024**3 )) ]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [ "$bytes" -gt $(( 1024**2)) ]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [ "$bytes" -gt 1024 ]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $safe_rename_pattern |
# safe_rename($1 = original file, $2 = target file) |
safe_rename() { |
local from="$1" |
local to="$2" |
# Extension |
local ext=$(sed -r 's/.*\.(.*)/\1/g' <<<"$to") |
# Basename without extension |
local b=$(basename "$2" ".$ext") |
# safe_rename_pattern is override-able, ensure it has a valid value: |
if ! grep -q '%e' <<<"$safe_rename_pattern" || |
! grep -q '%N' <<<"$safe_rename_pattern" || |
! grep -q '%b' <<<"$safe_rename_pattern" ; then |
safe_rename_pattern=$DEFAULT_SAFE_REN_PATT |
fi |
local n=1 |
while [ -f "$to" ]; do # Only executes if $2 exists |
to=$(sed "s/%b/$b/g" <<<"$safe_rename_pattern") |
to=$(sed "s/%N/$n/g" <<<"$to") |
to=$(sed "s/%e/$ext/g" <<<"$to") |
let 'n++'; |
done |
mv "$from" "$to" |
echo "$to" |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
for prog in mplayer convert montage bc ffmpeg mktemp ; do |
type -pf "$prog" >/dev/null |
if [ $? -ne 0 ] ; then |
error "Required program $prog not found!" |
let 'retval++' |
fi |
done |
return $retval |
} |
# Remove any temporal files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [ -z $TEMPSTUFF ]; then return 0 ; fi |
info "Cleaning up..." |
rm -rf ${TEMPSTUFF[*]} |
TEMPSTUFF=( ) |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
cleanup |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [ $verbosity -ge $V_ERROR ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 1; |
fi |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if plain_messages is overridden colour stops after the override |
echo "$1" >&2 ; tput sgr0 |
fi |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [ $verbosity -ge $V_WARN ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 3; |
fi |
echo "$1" >&2 ; tput sgr0 |
fi |
} |
# |
# Print an informational message |
# info($1 = text) |
info() { |
if [ $verbosity -ge $V_INFO ]; then |
if [ $plain_messages -eq 0 ]; then |
tput bold ; tput setaf 2; |
fi |
echo "$1" >&2 ; tput sgr0 |
fi |
} |
# |
# Same as info but with no colour ever. |
# infoplain($1 = text) |
infoplain() { |
if [ $verbosity -ge $V_INFO ]; then |
echo "$1" >&2 |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
VCSTEMPDIR=$(mktemp -d -t vcs.XXXXXX) |
if [ ! -d "$VCSTEMPDIR" ]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( "$VCSTEMPDIR" ) |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
local r=$(mktemp -p "$VCSTEMPDIR" "vcs-XXXXXX") |
if [ ! -f "$r" ]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF+=( "$r" ) |
echo "$r" |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 |
# globals: fromtime, totime, timecode_from, TIMECODES |
if [ $st -lt $fromtime ]; then |
st=$fromtime |
fi |
if [ $totime -gt 0 ] && [ $end -gt $totime ]; then |
end=$totime |
fi |
local inc= |
if [ "$tcfrom" -eq $TC_INTERVAL ]; then |
inc=$tcint |
elif [ "$tcfrom" -eq $TC_NUMCAPS ]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [ $tcnumcaps -eq 1 ]; then # Special case, just one capture, center it |
inc=$(( ($end-$st) / 2 + 1)) |
else |
inc=$(( ($end-$st) / $tcnumcaps )) |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [ $inc -gt ${VID[$LEN]} ]; then |
error "Interval is longer than video length, skipping $f" |
return $EX_USAGE |
fi |
local LTC=( ) stamp= |
for stamp in $(seq $st $inc $end); do |
LTC+=( $stamp ) |
done |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( ${TIMECODES[*]} ${LTC[*]} ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect() |
guess_aspect() { |
# mplayer's ID_ASPECT seems to be always 0 ¿? |
local w=${VID[$W]} h=${VID[$H]} |
if [ $w -eq 352 ]; then # VCD / DVD @ VCD Res. / Half-D1 / CVD |
if [ $h -eq 288 ] || [ $h -eq 240 ]; then |
aspect_ratio=4/3 |
elif [ $h -eq 576 ] || [ $h -eq 480 ]; then # Half-D1 / CVD |
aspect_ratio=4/3 |
fi |
elif [ $w -eq 704 ] || [ $w -eq 720 ]; then # DVD / DVB |
# Actually for 720x576/720x480 16/9 is as good a guess |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
aspect_ratio=4/3 |
fi |
elif [ $w -eq 480 ]; then # SVCD |
if [ $h -eq 576 ] || [ $h -eq 480 ]; then |
aspect_ratio=4/3 |
fi |
else |
warn "Couldn't guess aspect ratio." |
aspect_ratio=$(bc -lq <<<"$w / $h") |
fi |
local AR=$(sed -r 's/(\.[0-9]{2}).*/\1/g'<<<$aspect_ratio) |
info "Aspect ratio set to $AR" |
} |
# Capture a frame |
# capture($1 = filename, $2 = second) |
capture() { |
local f=$1 stamp=$2 |
local VIDCAPFILE=00000001.png |
# globals: $shoehorned $decoder |
if [ $decoder -eq $DEC_MPLAYER ]; then |
mplayer -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 1 -ss $stamp $shoehorned "$f" >/dev/null 2>&1 |
elif [ $decoder -eq $DEC_FFMPEG ]; then |
# XXX: For some reason -ss before -i failed on my mkv sample |
# while after -i it failed on my wmv9 sample ¿? |
ffmpeg -y -ss $stamp -i "$f" -an -dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $shoehorned $VIDCAPFILE >/dev/null 2>&1 |
else |
error "Internal error!" |
return $EX_SOFTWARE |
fi || { |
local retval=$? |
error "The capturing program failed!" |
return $retval |
} |
if [ ! -f "$VIDCAPFILE" ] || [ "0" == "$(du "$VIDCAPFILE" | cut -f1)" ]; then |
error "Failed to capture frame (at second $stamp)" |
return $EX_SOFTWARE |
fi |
return 0 |
} |
# Draw a timestamp in the file |
# apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
apply_stamp() { |
local filename=$1 timestamp=$2 width=$3 height=$4 |
local temp=$(new_temp_file ".png") |
mv "$filename" "$temp" |
# Add the timestamp to each vidcap, doing it here is much powerful/simple |
# than with the next montage command |
# Note the '!', it is necessary to apply aspect ratio change |
convert -box '#000000aa' -geometry ${width}x${height}! \ |
-fill $fg_tstamps -pointsize $pts_tstamps -gravity SouthEast \ |
-stroke none -strokewidth 3 -annotate +5+5 " $(pretty_stamp $stamp) " \ |
"$temp" "$filename" |
if [ ! -f "$filename" ]; then |
error "Failed to add timestamp to capture" |
mv "$temp" "$filename" # Leave things as before |
return $EX_CANTCREAT |
fi |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
# Note AFAIK sort only sorts lines, that's why y replace spaces by newlines |
local s=$1 |
sed 's/ /\n/g'<<<"$s" | sort -n | uniq |
} |
# Fills the $MPLAYER_CACHE and $VID variables with the video data |
# identify_video($1 = file) |
identify_video() { |
local f=$1 |
# Meta data extraction |
# Note to self: Don't change the -vc as it would affect $vdec |
MPLAYER_CACHE=$(mplayer -benchmark -ao null -vo null -identify -frames 0 -quiet "$f" 2>/dev/null | grep ^ID) |
VID[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # FourCC |
VID[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | cut -d'=' -f2) # Decoder (!= Codec) |
VID[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | cut -d'=' -f2) |
VID[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
VID[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"|cut -d'=' -f2|head -2|tail -1) |
# Upon consideration: |
#if grep -q '\.[0-9]*0$' <<<${VID[$FPS]} ; then |
# # Remove trailing zeroes... |
# VID[$FPS]=$(sed -r 's/(\.[1-9]*)0*$/\1/' <<<${VID[$FPS]}) |
# # ...And trailing decimal point |
# VID[$FPS]=$(sed 's/\.$//'<<<${VID[$FPS]}) |
#fi |
# Voodoo :P Remove (one) trailing zero |
if [ "${VID[$FPS]:$(( ${#VID[$FPS]} - 1 ))}" == "0" ]; then |
VID[$FPS]="${VID[$FPS]:0:$(( ${#VID[$FPS]} - 1 ))}" |
fi |
# Check sanity of the most important values |
is_number "${VID[$W]}" && is_number "${VID[$H]}" && is_number "${VID[$LEN]}" |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
local f=$1 |
local numcols=$cols |
if [ ! -f "$f" ]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
info "Processing $f..." |
identify_video "$f" || { |
error "Found unsupported value while identifying video. Can't continue." |
return $EX_SOFTWARE |
} |
# Vidcap/Thumbnail height |
local vidcap_height=$th_height |
if ! is_number "$vidcap_height" || [ "$vidcap_height" -eq 0 ]; then |
vidcap_height=${VID[$H]} |
fi |
if [ "0" == "$aspect_ratio" ]; then |
aspect_ratio=$(bc -lq <<< "${VID[$W]} / ${VID[$H]}") |
elif [ "-1" == "$aspect_ratio" ]; then |
guess_aspect |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local numsecs=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2 | cut -d. -f1) |
local nc=$numcaps |
# Contact sheet minimum cols: |
if [ $nc -lt $numcols ]; then |
numcols=$nc |
fi |
create_temp_dir |
# Compute the stamps (if in auto mode)... |
TIMECODES=${initial_stamps[*]} |
if [ $manual_mode -ne 1 ]; then |
compute_timecodes $timecode_from $interval $numcaps || { |
return $? |
} |
fi |
local base_montage_command="montage -font $font_tstamps -pointsize $pts_tstamps \ |
-gravity SouthEast -fill white " |
local output=$(new_temp_file '-preview.png') |
local VIDCAPFILE=00000001.png |
# If the temporal vidcap already exists, abort |
if [ -f $VIDCAPFILE ]; then |
error "Temporal vidcap file ($VIDCAPFILE) exists, remove it before running!." |
return $EX_CANTCREAT |
fi |
TEMPSTUFF+=( $VIDCAPFILE ) |
# Highlighs |
local hlfile="$VCSTEMPDIR/highlights.png" n=1 # Must be outside the if! |
if [ "$HLTIMECODES" ]; then |
local hlmontage_command="montage -gravity SouthEast -texture xc:LightGoldenRod " |
local hlcapfile= |
local pretty= |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if [ $stamp -gt $numsecs ]; then let 'n++' && continue ; fi |
pretty=$(pretty_stamp $stamp) |
info "Generating highlight #${n}/${#HLTIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
apply_stamp "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$hlcapfile" |
hlmontage_command+=" \"$hlcapfile\"" |
let 'n++' |
done |
#if [ "$title" ]; then |
# hlmontage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
#fi |
info "Composing highlights contact sheet..." |
eval "$hlmontage_command -geometry ${vidcap_width}x${vidcap_height}!+10+5 \ |
-tile ${numcols}x -shadow \"$hlfile\"" |
unset hlcapfile hlmontage_command pretty |
fi |
unset n |
# Normal captures |
# TODO: Don't reference $VIDCAPFILE |
local capfile= pretty= n=1 montage_command=$base_montage_command |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
# Note that it must be checked against numsecs and not endsec, to allow |
# the user manually setting stamps beyond the boundaries |
# This shouldn't occur automatically anymore with the new code. |
if [ $stamp -gt $numsecs ]; then let 'n++' && continue; fi |
info "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
apply_stamp "$VIDCAPFILE" $pretty $vidcap_width $vidcap_height || return $? |
capfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
# move to tempdir/<frame num>.png, cap num is padded to 6 characters |
mv "$VIDCAPFILE" "$capfile" |
montage_command+=" \"$capfile\"" |
let 'n++' # $n++ |
done |
unset capfile pretty n |
# geometry affects the source images, not the target one! |
# Note the file name could also be added by using the "-title" option, but I reserved |
# it for used set titles |
# FIXME: Title should go before the highlights |
montage_command+=" -geometry ${vidcap_width}x${vidcap_height}+10+5 -tile ${numcols}x -shadow" |
if [ "$title" ]; then |
montage_command+=" -font $font_heading -fill $fg_heading -title '$title'" |
fi |
montage_command+=" \"$output\"" |
info "Composing standard contact sheet..." |
eval $montage_command # eval is required to evaluate correctly the text in quotes! |
unset montage_command |
# Extended mode |
local extoutput= |
if [ "$extended_factor" != 0 ]; then |
# Number of captures. Always rounded to a multiplier of 2 |
# TODO: Round it to a multiplier of the number of columns |
local hlnc=$(bc -q <<<"( (${#TIMECODES[*]} * $extended_factor) / 2 * 2)") |
unset TIMECODES # required step to get the right count |
TIMECODES=${initial_stamps[*]} |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= montage_command=$base_montage_command |
extoutput=$(new_temp_file "-extended.png") |
# The image size of the extra captures is 1/4 |
let 'w=vidcap_width/2, h=vidcap_height/2' |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
info "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" $stamp || return $? |
apply_stamp "$VIDCAPFILE" $pretty $w $h || return $? |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
mv "$VIDCAPFILE" "$capfile" |
montage_command+=" \"$capfile\"" |
let 'n++' |
done |
montage_command+=" -geometry ${w}x${h}+5+2 -tile $(($numcols * 2))x -shadow" |
info "Composing extended contact sheet..." |
eval $montage_command "$extoutput" |
unset montage_command w h capfile pretty n |
fi |
# Codec "prettyfication" |
# Official FourCCs: <http://msdn2.microsoft.com/en-us/library/ms867195.aspx> |
# Unofficial list: <http://www.fourcc.org/> |
# Another software with a list of fourccs -> name mappings: |
# <http://webcvs.freedesktop.org/clipart/experimental/rejon/getid3/getid3/module.audio-video.riff.php?view=markup> |
local vcodec= acodec= |
case $( tr '[A-Z]' '[a-z]' <<<${VID[$VCODEC]}) in |
xvid) vcodec=Xvid ;; |
dx50) vcodec="DivX 5" ;; |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw RGB" ;; # How correct is this? |
avc1) vcodec="MPEG-4 AVC" ;; |
wmv1) vcodec="WMV7" ;; |
wmv2) vcodec="WMV8" ;; |
wmv3) vcodec="WMV9" ;; |
fmp4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
mp42) vcodec="MS MPEG-4 v2" ;; |
mpg4) vcodec="MS MPEG-4 v1" ;; |
mp43) vcodec="MS MPEG-4 v3" ;; |
div3) vcodec="DivX ;) Low Motion" ;; # Technically same as mp43 |
i420) vcodec="Raw I420" ;; # XXX: 420 presumably stands by 4:2:0 ? |
rv10) vcodec="RealVideo 1.0/5.0" ;; |
rv20) vcodec="RealVideo G2" ;; |
svq1) vcodec="Sorenson Video 1" ;; |
svq3) vcodec="Sorenson Video 3" ;; |
# These are known FourCCs that I haven't tested against so far |
wmva) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Unsupported. |
rv30) vcodec="RealVideo 8" ;; |
rv40) vcodec="RealVideo 9/10" ;; |
div4) vcodec="DivX ;) Fast Motion" ;; |
divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) |
iv50) vcodec="Indeo 5.0" ;; |
mjpg) vcodec="M-JPEG" ;; # XXX: Actually mJPG != MJPG |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
iv32) vcodec="Indeo 3.2" ;; |
msvc) vcodec="Microsoft Video 1" ;; |
mrle) vcodec="Microsoft RLE" ;; |
*) # If not recognized show FOURCC |
vcodec=${VID[$VCODEC]} |
;; |
esac |
if [ "${VID[$VDEC]}" == "ffodivx" ]; then |
vcodec+=" (MPEG-4)" |
elif [ "${VID[$VDEC]}" == "ffh264" ]; then |
vcodec+=" (h.264)" |
fi |
case $( tr '[A-Z]' '[a-z]' <<<${VID[$ACODEC]} ) in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg) DON'T! |
# vrbs = Vorbis in Matroska, probably other sane containers |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
*) # If not recognized show audio id tag |
acodec=${VID[$ACODEC]} |
;; |
esac |
if [ "${VID[$CHANS]}" ] && is_number "${VID[$CHANS]}" &&[ ${VID[$CHANS]} -ne 2 ]; then |
if [ ${VID[$CHANS]} -eq 0 ]; then |
# This happens e.g. in non-i386 when playing WMA9 at the time of |
# this writing |
warn "Detected 0 audio channels." |
warn " Does this version of mplayer support the audio codec ($acodec)?" |
elif [ ${VID[$CHANS]} -eq 1 ]; then |
acodec+=" (mono)" |
else |
acodec+=" (${VID[$CHANS]}ch)" |
fi |
fi |
local meta="Filename: $(basename "$f") |
File size: $(get_pretty_size "$f") |
Length: $(pretty_stamp "${VID[$LEN]}")" |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]} |
Format: $vcodec / $acodec |
FPS: ${VID[$FPS]}" |
local signature="$user_signature $user |
$PROGRAM_SIGNATURE" |
unset acodec vcodec |
if [ "$HLTIMECODES" ] || [ "$extended_factor" != "0" ]; then |
info "Merging contact sheets..." |
fi |
# If there were highlights then mix them in |
if [ "$HLTIMECODES" ]; then |
#\( -geometry x2 xc:black -background black \) # This breaks it! |
convert \( "$hlfile" -background LightGoldenRod \) \ |
\( "$output" \) -append "$output" |
fi |
# Extended captures |
if [ "$extended_factor" != 0 ]; then |
convert "$output" "$extoutput" -append "$output" |
fi |
info "Creating header and footer..." |
# Now let's add meta info |
# This one enlarges the image to add the text, and puts |
# meta info in two columns |
convert -font $font_heading -pointsize $pts_meta \ |
-background $bg_heading -fill $fg_heading -splice 0x$(( $pts_meta * 4 )) \ |
-gravity NorthWest -draw "text 10,10 '$meta'" \ |
-gravity NorthEast -draw "text 10,10 '$meta2'" \ |
"$output" "$output" |
# Finishing touch, signature |
convert -gravity South -font $font_sign -pointsize $pts_sign \ |
-background $bg_sign -splice 0x34+0-0 \ |
-fill $fg_sign -draw "text 10,3 '$signature'" "$output" "$output" |
if [ $output_format != "png" ]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$output_format" |
convert -quality $output_quality "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$(basename "$f").$output_format" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
info "Done. Output wrote to $output_name" |
cleanup |
} |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
info "Video Contact Sheet *NIX v${VERSION}, (c) 2007 Toni Corvera" "sgr0" |
} |
# Prints the list of options to stdout |
show_help() { |
local P=$(basename $0) |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-u|--user <arg> Set the username found in the signature to this. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found in |
/etc/passwd. |
-S|--stamp <arg> Add the image found at the timestamp "arg". Same format |
as -i. |
-l|--highlight <arg> Add the image found at the timestamp "arg" as a |
highlight. Same format as -i. |
-e[arg] | --extended=[arg] |
Enables extended mode and optionally sets the extended |
factor. By default it's $DEFAULT_EXT_FACTOR. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-a|--aspect <aspect> Aspect ration. Accepts floating point number or |
fractions. |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-j|--jpeg Output in jpeg (by default output is in png). |
-q|--quiet Don't print progess messages just errors. Repeat to |
mute completely even on error. |
-h|--help Show this text. |
Options used for debugging purposes or to tweak the internal workings: |
-M|--mplayer Force the usage of mplayer. |
-F|--ffmpeg Force the usage of ffmpeg. |
--shoehorn <arg> Pass "arg" to mplayer/ffmpeg. You shouldn't need it. |
-D Debug mode: Currently just prints the parsed |
commandline as the title and to stderr. |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), the resulting file will be called |
input.avi.png: |
\$ $P input.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes: |
\$ $P -i 3m30 input.avi |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
} |
# }}} # Core functionality |
#### Execution starts here #### |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
load_config |
# {{{ # Command line parsing |
# Based on getopt-parse.bash example. |
# On debian systems see </usr/share/doc/util-linux/examples/getopt-parse.bash.gz> |
# TODO: use no name at all with -u noarg |
#eval set -- "${default_options} ${@}" |
TEMP=$(getopt -s bash -o i:n:u:T:f:t:S:jhFMH:c:ma:l:De::UqAO: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg,help,"\ |
"shoehorn:,mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:,"\ |
"extended::,fullname,quiet,autoaspect,override:" \ |
-n $0 -- "$@") |
eval set -- "$TEMP" |
while true ; do |
case "$1" in |
-i|--interval) |
if ! interval=$(get_interval "$2") ; then |
error "Interval must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$interval" -le 0 ]; then |
error "Interval must be higher than 0, set to the default $DEFAULT_INTERVAL" |
interval=$DEFAULT_INTERVAL |
fi |
timecode_from=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
if ! is_number "$2" ; then |
error "Number of captures must be (positive) a number! Got '$2'." |
exit $EX_USAGE |
fi |
if [ $2 -eq 0 ]; then |
error "Number of captures must be greater than 0! Got '$2'." |
exit $EX_USAGE |
fi |
numcaps="$2" |
timecode_from=$TC_NUMCAPS |
shift # Option arg |
;; |
-u|--username) user="$2" ; shift ;; |
-U|--fullname) |
user=$(grep ^$(id -un): /etc/passwd | cut -d':' -f5 |sed 's/,.*//g') |
if [ -z "$user" ]; then |
user=$(id -un) |
error "No fullname found, falling back to default ($user)" |
fi |
;; |
-T|--title) title="$2" ; shift ;; |
-f|--from) |
if ! fromtime=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
shift |
;; |
-t|--to) |
if ! totime=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$totime" -eq 0 ]; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
initial_stamps=( ${initial_stamps[*]} $temp ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES+=( $temp ) |
shift |
;; |
-j|--jpeg) output_format=jpg ;; |
-h|--help) show_help ; exit $EX_OK ;; |
--shoehorn) |
shoehorned="$2" |
shift |
;; |
-F) decoder=$DEC_FFMPEG ;; |
-M) decoder=$DEC_MPLAYER ;; |
-H|--height) |
if ! is_number "$2" ; then |
error "Height must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
th_height="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
aspect_ratio="$2" |
shift |
;; |
-A|--autoaspect) aspect_ratio=-1 ;; |
-c|--columns) |
if ! is_number "$2" ; then |
error "Columns must be a (positive) number. Got '$2'." |
exit $EX_USAGE |
fi |
cols="$2" |
shift |
;; |
-m|--manual) manual_mode=1 ;; |
-D) echo "Command line: $0 $*" && title="$0 $*" ; ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
# XXX: For some reason parsing of floats gives an error, so for now |
# ints and only fractions are allowed |
if [ "$2" ] && ! is_float "$2" && ! is_fraction "$2" ; then |
error "Extended multiplier must be a (positive) number (integer, float "\ |
"or fraction)." |
error " Got '$2'." |
exit $EX_USAGE |
fi |
if [ "$2" ]; then |
extended_factor="$2" |
else |
extended_factor=$DEFAULT_EXT_FACTOR |
fi |
shift |
;; |
-O|--override) |
# Rough test |
if ! egrep -q '[a-zA-Z_]+=[^;]*' <<<"$2"; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
override "$2" "command line" |
shift |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [ $verbosity -gt $V_ERROR ]; then |
verbosity=$V_ERROR |
else |
verbosity=$V_NONE |
fi |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $@)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Remaining arguments |
if [ ! "$1" ]; then |
show_help |
exit $EX_USAGE |
fi |
# Test requirements |
test_programs || exit $EX_UNAVAILABLE |
# If -m is used then -S must be used |
if [ $manual_mode -eq 1 ] && [ -z $initial_stamps ]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
exit $EX_USAGE |
fi |
set +e # Don't fail automatically |
for arg do process "$arg" ; done |
# }}} # Command line parsing |
# vim:set ts=4 ai: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/tags/1.0.6b |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/AUTHORS |
---|
0,0 → 1,13 |
Copyright 2007-2016 Toni Corvera |
Patches by Eris Belew (2014): |
- Fixes for PKGBUILD for newer Arch systems |
- Fix for potentially problematic unwrapped grep pattern |
Patches by Phil Grundig (2008): |
- Support for array/string operations on bash 2.05b |
[no longer part of the script] |
- Workaround for mplayer's first frame getting dropped |
- Timestamp printing fixes |
- Removal of ms for mplayer's stamps |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/CHANGELOG |
---|
0,0 → 1,503 |
1.13.2 (?): |
* BUGFIX: Fixed number of captures exceeded by one with mplayer [#225] |
Reported by Miya |
* OTHER: (BUGFIX in prereleases) |
Fixed error when processing files with quotes in the file name |
[#226] |
* OTHER: Allow disabling coloured output altogether. [#311] |
1.13.1 (2014-02-26): |
* BUGFIX: Fixed uncommon bug with unwrapped grep string [#217] |
Submitted by Eris Belew |
* OTHER: Adapt PKGBUILD to new guidelines [#219] |
Submitted by Eris Belew |
1.13 (2013-03-08): |
* Complete manual pages |
* Added 'anonymous' to the list of settings |
* Remove meaningless decimals when generating config files |
* New setting: 'profiles', allows loading profiles automatically and also |
loading profiles from other profiles |
* Change also title colours in 'black' and 'white' profiles |
* Codec identification for Fraps captures [#179] |
* New setting 'capturer' deprecates 'decoder'. Uses actual names (ffmpeg and |
mplayer) instead of variables ($DEC_FFMPEG and $DEC_MPLAYER) |
* Changed default verbosity level to INFO (same output as before) |
* BUGFIXES: |
- Make "dynamic" settings case-insensitive, i.e. |
bg_heading=$bg_contact can also be written bg_heading=$BG_CONTACT |
- Correct extended-set resizing |
- Constraint checking of settings failed silently for alias-only names |
- Code typo: Produced error message when extended mode was narrower than |
contact sheet |
- Only warned about command-line GETOPT override when using uppercase |
setting name |
- Fixes for FreeBSD compatibility (regressions introduced in 1.12.3, |
[#189]): |
> Wrong parsing of floats and positions/percentages on |
FreeBSD's bash 4.0.10 (FreeBSD only) |
> Unsupported 'expr match' replaced by awk |
- Fix error when avoiding repeated captures |
- Don't filter cached captures more than once [#199] |
- Skip files where interval gets rounded to zero [#195] |
* Scheduled code cleanup: |
- Removal of deprecated configuration options: DEFAULT_END_OFFSET, |
shoehorned and safe_rename_pattern |
- Removal of deprecated option '--undocumented shoehorn' |
- Deprecation of '--end_offset' ('--end-offset' should be used instead) |
* COSMETIC: |
- Add '(h.264)' to ffmpeg video codec id when appropriate |
- Correct "Capturing in range..." message |
- Refer to configuration variables as "settings" |
- Print informational messages for each funky mode |
- Pretty-print timestamps when doing safe-length measuring [#177] |
- Colourised tracing |
* OTHER: |
- Help rewordings and clarification |
- Help fixes: |
- Old DVD mode description was still displayed |
- Incorrectly had `--jpeg 2' instead of `--jpeg2' or `--jpeg=2' |
- Added new distribution profile: compact |
- Added new example profiles (black-mosaic and black-compact-chain), the |
latter demonstrating how a profile can load other profiles |
- List also builtin profiles with --profile :list |
- Each profile can no longer be loaded more than once |
- Restore terminal through stty [#198] |
* UNDOCUMENTED/DEBUG: |
- Undocumented options: |
- Don't fail on unknown sub-options |
- New sub-options: trace, display and discard |
- Debugging facility: --undocumented trace=funcname |
- Display $POSIXLY_CORRECT and sed's path in 'vcs -DD' output |
- Display awk and sed versions, if possible, in 'vcs -DD' output |
* INTERNAL: |
- Check ImageMagick through convert instead of identify |
- Don't run filters in subshells |
- Fix some typos |
- Bugfix: Actually use passed timestamp in filt_apply_timestamp() |
- Bugfix: Don't accept --shoehorn (was deprecated and unhandled) |
- Set LANG to C |
- Added simeq() and '~' fptest operator |
- New (4th iteration) interval parsing code, single sed command, |
more strict checking of PRE |
1.12.3 (2011-07-17): |
* BUGFIX: Actually handle --ffmpeg and --mplayer [#169] |
* BUGFIX: Correct parsing of -U [#187] |
* OTHER: |
- Fix printing of remaining options on command-line error |
- Switch to a minimum of bash 3.1 [#173] |
- Avoid re-capturing the same frame twice [#122] |
- Use getent instead of /etc/passwd when available |
* INTERNAL: |
- Use of Bash's 'caller' in 'assert' and 'trace' |
- 'assert' prints a call trace on error |
- 'assert_if' |
- Don't use mplayer's length as a ceil for timecode removal [#174] |
1.12.2 (2010-08-24): |
* BUGFIX: Fix cleanup of temporary files (regression since 1.11.2). [#167] |
Submitted by Jason Tackaberry. |
* FEATURES: |
- Added 'fg_all', 'bg_all' and 'font_all' config variables. [#156] |
- Added 'nonlatin_filenames' config variable. [#159] |
- Added identification for VP8 (WebM). [#166] |
* OTHER: |
- Print variable names in lowercase when using --generate. |
1.12.1 (2010-04-23): |
* BUGFIXES: |
- Workaround for cases in which GAWK uses comma as decimal separator. |
Any OS with GAWK 3.1.3 to 3.1.5 was affected (where the environment |
language uses commas, e.g. Debian Lenny with many European languages) |
- Don't try to go on in DVD mode with unreadable ISOs |
1.12: (2010-04-10) |
* New features/tweaks: |
- Loading of random configuration files (--config / -C) |
- Profiles: Similar to above but simpler syntax (--profile / -p) |
- Config/Profile generation from command-line (--generate) |
- Adapt heading, title and footer height to font size (fonts that used |
to get cropped should now be fine) |
* DVD mode cleanup: |
- Command-line switched to match "normal" files: |
Before: |
$ vcs --dvd /dev/dvd 0 or $ vcs --dvd /dev/dvd 1 |
Equivalents now: |
$ vcs --dvd /dev/dvd or $ vcs --dvd --dvd-title 1 /dev/dvd |
* New end-offset behaviour: |
- A 5.5% end offset is applied by default |
- Can be disabled with -E0 or end_offset=0 |
- MIN_LENGTH_FOR_END_OFFSET is no longer used |
* Configuration files cleanup: |
- Simplified or more meaningful names where appropriate (the older |
names will continue to work for a while, and users will be warned) |
"vcs --generate" with no other arguments can be used to translate them |
- Validation of configuration options. |
Incorrect values will be discarded and an error shown; processing will |
continue. |
- Configuration searched in ~/.vcs/vcs.conf too |
- Syntax enhancements: |
> Comments can now be included in-line |
> Putting '#' in a value now requires using the "escaped form" '$#' |
> Semicolons (;) also serve to start comments: When one is found the |
rest of the line is ignored, they continue to be disallowed in values |
i.e. 'tl;dr' will be parsed as 'tl' |
* Other: |
- Accept timecodes and percentages in end_offset, both from the |
command-line and in configuration files |
- Print the start and end timestamps in effect before capturing |
- No longer accept interval zero (used to be re-set to default) |
- Tighter printing of overrides and no longer printed as warning |
- Strickter handing of wrong options |
- Fall back to Helvetica also when no fonts dir is located. Look |
in /usr/local too. |
- --end-offset added as an alias to --end_offset |
- Starting with 1.12 a tarball + makefile is also provided |
* BUGFIXES: |
- Avoid possible (unlikely) usage of scientific notation in internal |
calculations |
- Distinguish between default end offset and user's end offset with the |
same value |
- Handle --nonlatin correctly |
- DVD Mode + FFmpeg identification: Check VOB #0 instead of #1 |
- Don't print escape codes to stdout when testing colour printing |
* Options removed: |
--shoehorn, temporary replacement: --undocumented shoehorn. Will be gone |
in 1.13 |
--mincho, replaced by --nonlatin since 1.11 |
MIN_LENGTH_FOR_END_OFFSET, as explained above, no longer needed |
* INTERNAL: |
- $CFGFILE replaced by ~/.vcs.conf |
- Use -p for profiles instead of -P (used, undocumented, in 1.11) |
1.11.2: (2010-03-19) |
* Added Cook and Sipro (RealAudio 4, 5 & 6) codecs |
* BUGFIXES: |
- Remove extra, empty, temporary dir |
- Use standard awk syntax for exponentiation (pyth_th) |
- Workaround for systems that don't register fonts with ImageMagick |
* DEBUG: Print to stderr when probbing with mplayer too |
1.11.1: (2010-03-11) |
* Added FLV1 codec |
* BUGFIXES: |
- Deprecate DEFAULT_INTERVAL, DEFAULT_NUMCAPS and DEFAULT_COLS as |
overrides, warn about their new names (interval, numcaps and cols) |
- Fix ImageMagick version detection |
1.11: (2010-03-07) |
* FEATURES |
- Allow setting output filename. With extension will set output format, |
without will inherit it. |
- Allow percentages in height. |
- Require mplayer OR ffmpeg instead of both. Having both is still |
recommended for better results. |
- Safe mode, for files whose length doesn't get reported correctly. |
Completely automated. |
Number of tries can be increased with -Ws. Repeat to increase further. |
Use -WS to do try as many times as possible. |
Accuracy (stepping) can be increased with -Wp. Repeat to increase |
accuracy. Decrease with -WP. |
Can be deliberately disabled with -Wb to force processing of broken |
files. *VCS WITH -Wb WILL FAIL ON BROKEN FILES* |
- Added -dp (--disable padding) equivalent to overriding HPAD to 0 |
* BUGFIXES: |
- Don't pass ms to mplayer. It ignores them anyway and in some rare |
cases breaks the last capture (possibly due to the 5-frames hack) |
- Honor detected aspect ratio if found |
- Try to detect files that might fail on the last capture and trigger |
safe mode |
- Timestamps font was being ignored. As a side effect this produced |
italiced timestamps in some systems |
- Fixed obscure bug with safe_rename_pattern overrides |
* COMPAT: Support for bash 2.05b. This will (probably) be the last version |
capable of running under bash 2. |
* DVD mode revamp |
- Print title file size instead of disc size when possible |
- Aspect ratio detection, if available |
- Use of FFmpeg if available to get better information |
- Mostly x-platform, only ISOs identification is a bit better in Linux |
* Added FourCCs: 3IV1, 3IV2 (3ivx); s263 (H.263); mp4v, MP4V, H264 |
(MPEG-4 and AVC in mov/mp4), VP6F (VP6 Flash Version), AMR |
Video codec renamings: |
- TechSmith codec name shortened to TechSmith SCC |
- Raw RGB renamed to Raw video |
* Help cleanup. The default help output is much shorter, the full text |
can be displayed with --fullhelp. Also print the decoder choice near |
the appropriate option (-M/-F) |
* Added --anonymous to help (never was in it) |
* Drop requirement on seq/jot and bc, replaced by inline awk |
... New requirement: Perl (only for DVDs). |
* Adopt new/fixed numbering scheme |
<http://p.outlyer.net/dox/vcs:devel:renumbering> |
* Check ImageMagick version (must decide which is the real minimum |
required) |
* Non-latin fonts revamp: |
- -I no longer works alone (use -Ij or -Ik instead) |
- -Ik, -Ij and --nonlatin try to pick an appropriate font automatically |
- -I accepts a font name or font filename like |
-Ij=Kochi-Mincho-Regular or |
-Ij=/usr/share/fonts/truetype/kochi/kochi-mincho.ttf |
* Deprecated options: |
--shoehorn: Will be removed unless someone really needs it. |
--mincho: Replaced by --nonlatin |
* COSMETIC: |
- Default font switched to DejaVu Sans. |
Font sizes reduced to accomodate the new default. |
Should fall back to a sane default if it's not available |
- Much tighter padding |
- Smaller timestamps font by default |
- Print friendlier timestamp when a capture fails |
- Print program signature to console without colour |
- Use main font by default in timestamps |
- Heading background colour toned down |
- Added colourised output when tput is not capable (i.e. FreeBSD) |
- Added prefixes when colour is not available for console output |
- Don't print lsdvd error channel is DVD mode |
- Suppress mv errors (e.g. over VFS being unable to preserve) |
* Minimum ImageMagick version set to 6.3.5-7 |
* Better detection of requirements (e.g. disallow decoders without png |
support) |
* Allow overriding height, number of captures, interval, columns, and |
padding |
* UNDOCUMENTED/DEBUG: |
- Allow stopping the main loop before cleaning up (--undocumented hang) |
- Identification-only mode. Might be promoted to an actual feature |
(--undocumented idonly) |
- Allow setting ffmpeg and mplayer path (--undocumented set_ffmpeg and |
set_mplayer) |
- Allow disabling either mplayer of ffmpeg (as if they weren't |
installed (--undocumented disable_ffmpeg and disable_mplayer) |
- Added -Wc to disable console colour, repeat to disable prefixes |
* INTERNAL: |
- assert() |
- Cleanup: correctness checks converted to asserts, removal of old dead |
code |
- Typos |
1.0.100a: (2009-04-10) (1.10) |
* FEATURE: FreeBSD (7.1-RELEASE) support |
* COMPATIBILITY: |
- Call bash through env |
- Ensure we're using the correct getopt version |
- Try to use POSIX sed options when appropriate |
- Replaced incompatible sed constructs |
- Use mktemp's common GNU/BSD(/POSIX?) syntax |
- Use jot instead of seq if required and available |
* BUGFIX: Don't fail if tput is unable to change colours |
* BUGFIX: Check for requirements before anything else |
* INTERNAL: Cache tput output |
* FEATURE: Added -R / --randomsource. Mainly useful for debugging, |
also to repeat a set of results and compare outputs on different |
systems |
* Corrected info message in photos mode |
1.0.99: (2009-03-11) (1.9) |
* FEATURE: Experimental support for DVDs (-V) |
* FEATURE: Added JPEG 2000 output format (-j2) |
* FEATURE/COSMETIC: Polaroid mode now produces a polaroid-like frame, the |
older version is now renamed as simply 'photos' |
New "funky" modes: newer polaroid, photos (older polaroid), |
polaroidframe |
* Overrideable variables: DISABLE_SHADOWS and DISABLE_TIMESTAMPS (set to 1 |
to disable) |
* BUGFIX/COSMETIC: Re-added the missed space before filename |
* BUGFIX/COSMETIC: Reworked alignment and padding |
* Timestamps size is adjusted with smaller captures |
* BUGFIX: Fixed polaroid/rotate bug where all images overlapped on the same |
position (reported by Aleksandar Urošević, formerly unreproducible) |
* Better detection of video/audio features by falling back to ffmpeg when |
appropriate |
1.0.12: (2008-04-16) (1.8) |
* BUGFIX/COSMETIC: Corrected 0ms timestamps |
* COSMETIC: Re-added the (disabled for long) black border after highlights |
* BUGFIX/COSMETIC: Corrected the count of captures in manual-only mode (-m) |
* FEATURE: Added a minimun length to use the end offset |
* BUGFIX: Fixed the regression on highlights from the last version (extra |
padding was being added by IM automatically) |
* INTERNAL: Simplified use of IM's identify |
* BUGFIX: Fixed parsing of manual timestamps including milliseconds |
(when seconds didn't include the s character they were accidentally |
multiplied by 10!) |
1.0.11: (2008-04-08) (1.7) |
* BUGFIX: (brown bag bug) Corrected typo in variable name that made vcs |
fail when setting the default timecode derivation to number of |
captures instead of interval (i.e. when including timecode_from=8 in |
the config file) (thanks to Chris Hills for the bug report) |
* WORKAROUND: Fix for all-equal captures (seems to be a known problem |
with mplayer [M1]) (contributed by Phil Grundig) |
* RETROCOMPATIBILITY: Older bash syntax for appending and initialising |
arrays (contributed by Phil Grundig) |
* COMPATIBILITY: Support alternative du syntax for compatibility with |
busybox (based on Phil Grundig's contribution) |
* COSMETIC: Don't print milliseconds when using mplayer as capturer |
(they're not really meaningful then) (suggested by Phil Grundig) |
* COSMETIC: Align the extended set captures (-e) and the standard set |
(bug pointed by Chris Hills). Seems to fail at some (smaller?) |
sizes. |
"Funky" modes aren't correctly aligned yet. |
* DEBUGGING: Added optional function call trace (by setting variable DEBUG |
to 1) |
* Added FOURCC for VC-1 |
* COSMETIC: Fixed captures recount with multiple files (prompted by a |
bugreport from Dougn Redhammer) |
1.0.10: (2007-11-08) (1.6) |
* BUGFIX: Corrected aspect guessing bug: would fail if width was standard |
but height not |
* FEATURE: Allow explicitly disabling timestamps (-dt or --disable |
timestamps) |
* FEATURE: Allow explicitly disabling shadows (-ds or --disable shadows) |
* Added HD resolution guessed aspect ratio (defaults to 16/9) |
* OTHER: Changed e-mail address in the comments to gmail's, would probably |
get a quicker response. |
1.0.9a: (2007-06-10) (1.5.2, -Brown bag- Bugfix release) |
* BUGFIX: Fixed regression introduced in 1.0.8a: unsetting numcols |
broke extended mode captures (Thanks to 'Aleksandar Urošević'). |
* BUGFIX: Use the computed number of columns for extended mode |
(instead of the global one) |
1.0.8a: (2007-06-02) (1.5.1, Bugfix release) |
* BUGFIX: User set number of columns wasn't being used if -n wasn't used |
(Thanks to 'Homer S'). |
* BUGFIX: Right side of heading wasn't using the user's font colour |
(Thanks to 'Dougn Redhammer'). |
1.0.7a: (2007-05-12) (1.5) |
* Print title *before* the highlights. |
* Added the forgotten -O and -c to the help text (oops!) |
* Experimental: Allow using non-latin alphabets by switching font. See -I. |
It only affects the filename! Also allow overriding the font to be used |
to print the filename ($font_filename). Right now only using a Mincho |
font, it can be overriding by overriding $FONT_MINCHO. |
* Make title font size independent of the timestamps size. And allow |
overriding the title font ($font_title), font size ($pts_title) |
and colours ($fg_title and $bg_title). |
* Allow overriding the previews' background ($bg_contact) |
* Added getopt, identify, sed, grep and egrep to the checked programs |
* BUGFIX: Corrected test of accepted characters for intervals |
* INTERNAL: New parsing code |
* FEATURE: Replaced hard by soft shadows |
* BUGFIX: Corrected console colour usage: Print the colours to the correct |
channel |
* Made tput (coloured console output) optional (AFAIK should be present in |
any sane system though). |
* FEATURE: Funky modes (more to come...): Polaroid, Film (rough, initial, |
version), Photoframe and Random colours/fonts. (see --help) |
* INTERNAL: Use /dev/shm as base tempdir if possible |
* BUGFIX: Fixed safe_rename(): Don't assume current dir, added '--' to mv |
* Added workaround for ffmpeg arguments order |
* Allow getting the output of ffmpeg/mplayer (with $stdout and $stderr) |
* INTERNAL: Renamed info() to inf() to eliminate ambiguities |
* INTERNAL: guess_aspect() doesn't operate globally |
* Reorganized help by alphabetical/rarity order |
* FEATURE: Full milliseconds support (actually, full decimal point seconds), |
timecode format extended to support e.g. 3m.24 (which means 00:03:00.240) |
* BUGFIX/FEATURE: The number of extended captures is rounded to match the |
standard columns (extended width matches standard) |
* Made FOURCCs list case sensitive (the list has grown enough that I no |
longer see a benefit in being ambigous) |
* Added codec ids for On2's VP3, VP4, VP5 and VP6, TechSmith Screen Capture |
Codec (Camtasia's) and Theora, expanded list of FOURCCs of Indeo's |
codecs. |
* Added -E / --end_offset / $DEFAULT_END_OFFSET, used to eliminate some |
seconds from the end |
1.0.6b: (2007-04-21) (1.4.1, Bugfix release) |
* BUGFIX: Use mktemp instead of tempfile (Thanks to 'o kapi') |
* Make sure mktemp is installed, just in case ;) |
1.0.5b: (2007-04-20) (1.4) |
* INTERNAL: Split functionality in more separate pieces (functions) |
* BUGFIX: Corrected --aspect declaration |
* CLEANUP: Put all temporary files in the same temporary directory |
* FEATURE: Highlight support |
* FEATURE: Extended mode (-e) |
* FEATURE: Added -U (--fullname) |
* Requirements detection now prints all failed requirements |
* BUGFIX: (Regression introduced in 1.0.4b) Fail if interval is longer |
than video |
* Don't print the success line unless it was really successful |
* Allow quiet operation (-q and -qq), and different verbosity levels |
(only through config overrides) |
* Print vcs' identification on operation |
* FEATURE: Auto aspect ratio (-A, --autoaspect) |
* INTERNAL: Added better documentation of functions |
* Print coloured messages if possible (can be disabled by overriding |
$plain_messages) |
* FEATURE: Command line overrides (-O, --override) |
* BUGFIX: Don't allow setting -n0 |
* Renamed codec ids of WMA2 (to WMA8) and WMA3 (to WMA9) |
* Changed audio codec ids from MPEG-1 to MPEG, as there's no difference, |
from mplayer's identification at least, between MPEG-1 and MPEG-2 |
* Audio identified as MP2 can also actually be MP1, added it to the codec id |
* Added codec ids for: Vorbis, WMA7/WMA1, WMV1/WMV7, PCM, DivX ;), |
OpenDivX, LAVC MPEG-4, MSMPEG-4 family, M-JPEG, RealVideo family, I420, |
Sorenson 1 & 3, QDM2, and some legacy codecs (Indeo 3.2, Indeo 5.0, |
MS Video 1 and MS RLE) |
* Print the number of channels if != 2 |
1.0.4b: (2007-04-17) (1.3) |
* Added error checks for failures to create vidcap or to process it |
convert |
* BUGFIX: Corrected error check on tempdir creation |
* BUGFIX: Use temporary locations for temporary files (thanks to |
Alon Levy). |
* Aspect ratio support (might be buggy). Requires bc. |
* Added $safe_rename_pattern to allow overriding the default alternate |
naming when the output file exists |
* Moved previous previous versions' changes to a separate file. |
* Support for per-dir and system-wide configuration files. Precedence |
in ascending order: |
/etc/vcs.conf ~/.vcs.conf ./vcs.conf |
* Added default_options (broken, currently ignored) |
* BUGFIX: (Apparently) Corrected the one-vidcap-less/more bug |
* Added codec ids of WMV9 and WMA3 |
1.0.3b: (2007-04-14) (1.2.1, Brown bag hotfix) |
* BUGFIX: Don't put the full video path in the heading |
1.0.2b: (2007-04-14) (1.2) |
* Licensed under LGPL (was unlicensed before) |
* Renamed variables and constants to me more congruent |
* Added DEFAULT_COLS |
* BUGFIX: Fixed program signature (broken in 1.0.1a) |
* Streamlined error codes |
* Added cleanup on failure and on delayed cleanup on success |
* Changed default signature background to SlateGray (blue-ish gray) |
1.0.1a: (2007-04-13) (1.1) |
* Print output filename |
* Added manual mode (all timestamps provided by user) |
* More flexible timestamp format (now e.g. 1h5 is allowed (means 1h 5secs) |
* BUGFIX: Discard repeated timestamps |
* Added "set -e". TODO: Add more verbose error messages when called |
programs fail. |
* Added basic support for a user configuration file. |
1.0a: (2007-04-10) (1.0) |
* First release keeping track of history |
* Put vcs' url in the signature |
* Use system username in signature |
* Added --shoehorn (you get the idea, right?) to feed extra commands to |
the cappers. Lowelevel and not intended to be used anyway :P |
* When just a vidcap is requested, take it from the middle of the video |
* Added -H|--height |
* Added codec ids of WMV8 and WMA2 |
0.99.1a: Interim version, renamed to 1.0a |
0.99a: |
* Added shadows |
* More colourful headers |
* Easier change of colours/fonts |
0.5a: * First usable version |
0.1: * First proof of concept |
# vim:set ts=3 sw=3 et textwidth=80: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/vcs |
---|
0,0 → 1,5236 |
#!/usr/bin/env bash |
# |
# $Rev$ $Date$ |
# |
# vcs |
# Video Contact Sheet *NIX: Generates contact sheets (previews) of videos |
# |
# Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Toni Corvera |
# |
# This program is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This program is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the Free Software |
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
# |
# Author: Toni Corvera <outlyer@gmail.com> |
# |
# (Note: The references that used to be here have been moved to |
#+ <http://p.outlyer.net/dox/vcs:devel:references>) |
# |
# The full changelog can be found at <http://p.outlyer.net/vcs/files/CHANGELOG> |
declare -r VERSION="1.13.2" |
declare -r RELEASE=0 |
declare -ri PRERELEASE=4 # This version is retrofitted, it was never actually made public |
[ "$RELEASE" -eq 1 ] || declare -r SUBVERSION="-pre.${PRERELEASE}" |
# NOTE: This pre-release is a quick modification, it just adds a way of disabling |
# coloured output altogether, by setting TERM to a monochrome terminal type. |
# e.g. $ env TERM=vt100 vcs -Wc |
set -e |
# GAWK 3.1.3 to 3.1.5 print decimals (with printf) according to locale (i.e. |
#+decimal comma separator in some locales, which is apparently POSIX correct). |
#+Older and newer versions, though, need either POSIXLY_CORRECT to be set (even |
#+be empty), --posix or --use-lc-numeric to honour locale. |
# MAWK appears to always use dots. |
# Info: <http://www.gnu.org/manual/gawk/html_node/Conversion.html> |
#export POSIXLY_CORRECT=1 # Immitate behaviour in newer gawk |
export LC_NUMERIC=C |
# All output from tools is either removed or parsed. |
# Standardise on the C locale. |
export LANG=C |
export LC_COLLATE=C # Ensure collation (e.g. tr a-z A-Z) works as expected |
# Fail soon if this version of bash is too old for the syntax, don't expose bash to the newer |
# syntax |
# See the "Bash syntax notes" section for details |
[ "$BASH_VERSINFO" ] && { |
# Absolute minimum right now is 3.1 |
if [ "${BASH_VERSINFO[0]}" -lt 3 ] || |
[ "${BASH_VERSINFO[0]}" -eq 3 -a "${BASH_VERSINFO[1]}" -lt 1 ]; then |
echo "Bash 3.1 or higher is required" >&2 |
exit 1 |
fi |
} |
# {{{ # TO-DO |
# * [[x1]] Find out why the order of ffmpeg arguments breaks some files. |
# * Change default DVD_TITLE to 0 |
# * Deprecation schedule: |
# DEPRECATED FROM | EXPECTED REMOVAL | DESCRIPTION |
# ------------------|------------------|------------------------------------------------------ |
# 1.12 1.14 Old names for settings renamed in 1.12. |
# output_format, plain_messages, th_height, |
# hpad, font_mincho |
# In 1.13 the new names start to be used internally. |
# -------------------------------------------------------------------------------------------- |
# 1.13 1.14 --end_offset -> --end-offset |
# 1.13 1.14 auto-loading ./vcs.conf (lesser version of profiles) |
# -C :pwd will stay |
# -------------------------------------------------------------------------------------------- |
# ? ?+1 decoder. Replaced by capturer, the syntax changes |
# ? ?+1 --funky -> --profile |
# * Variables cleanup: |
# Variables will use a more uniform scheme, with prefixes where appropriate: |
# - INTERNAL_*: Used internally to adapt messages and the like to the input |
# - UNDFLAG_*: Undocumented flags. Used internally to keep track of undocumented modes (-Z) |
# - USR_*: Holds values of variables as set by the user, either from overrides or from the |
# command-line. |
# implementation |
# - Global variables will be capitalised while local variables will be lowercase |
# - Setting names (configuration file variables) will be case insensitive, but always |
# displayed and documented in lowercase |
# * Optimisations: |
# - Reduce the number of forks/subshells |
# * Portability notes |
# - 'sed -r' is not portable, works in GNU, FreeBSD equivalent -E |
# - 'grep -o' is not portable, works in GNU and FreeBSD |
# Alternatives: |
# > One match per line: |
# $ sed -n -e 's/.*\(SEARCH\).*/\1/gp |
# > Multiple matches per line: (like grep -o) |
# $ sed -n -e 's/\(SEARCH\)/\1\ |
# /gp' | sed -e 's/.*\(SEARCH\).*/\1/' -e '/SEARCH/!d' |
# The p flag ONLY prints IF a substition succeeded |
# - 'expr' is not a builtin, 'expr match' is not understood in, at least, FreeBSD |
# expr operations should have equivalent bash string manipulation expressions |
# - 'egrep' is deprecated in SUS v2, 'grep -E' replaces it [[x2]] |
# * UNIX filter equivalencies |
# - cut -d: -f1 === awk -F: '{print $1}' === awk '{BEGIN FS=":"}; {print $1}' |
# - grep -v pattern === sed '/pattern/d' |
# }}} # TO-DO |
# {{{ # Constants |
# Use configuration files to modify the behaviour of the |
# script. Using them allows overriding some variables (see below) |
# to your liking. Only lines with a variable assignment are evaluated, |
# it should follow bash syntax, note though that ';' can't be used |
# currently in the variable values; e.g.: |
# |
# # Sample configuration for vcs |
# user=myname # Sign all compositions as myname |
# bg_heading=gray # Make the heading gray |
# |
# There is a total of four configuration files than are loaded if the exist: |
# * /etc/vcs.conf: System wide conf, least precedence |
# * ~/.vcs.conf: Per-user conf, second least precedence |
# * ~/.vcs/vcs.conf: Per-user conf, alternate location for more complex configs |
# * ./vcs.conf: Per-dir config, most precedence (deprecated) |
# |
# The variables that can be overriden are below the block of constants ahead. |
# Default value for INTERVAL, setting interval to 0 also re-sets it to this value |
declare -ri DEFAULT_INTERVAL=300 |
# see $DECODER |
declare -ri DEC_MPLAYER=1 DEC_FFMPEG=3 |
# See $TIMECODE_FROM |
declare -ri TC_INTERVAL=4 TC_NUMCAPS=8 |
# These can't be overriden, modify this line if you feel the need |
declare -r PROGRAM_SIGNATURE="Video Contact Sheet *NIX ${VERSION}${SUBVERSION} <http://p.outlyer.net/vcs/>" |
# Filename pattern for safe renaming (appending numbers until finding a name |
#+not in use). |
# Since 1.13 no longer configurable. Don't mess with it too much. |
# By default "%b-%N.%e" where: |
# %b is the basename (file name without extension) |
# %N is the appended number |
# %e is the extension |
# Will first try %b.%e, then %b-1.%e, %b-2.%e and so on, i.e. |
#+creates outputs like "output.avi-1.png" |
declare -r SAFE_RENAME_PATTERN="%b-%N.%e" |
# see $EXTENDED_FACTOR |
declare -ri DEFAULT_EXT_FACTOR=4 |
# see $VERBOSITY |
declare -ri V_ALL=5 V_NONE=-1 V_ERROR=1 V_WARN=2 V_INFO=3 |
# Indexes in $VID |
declare -ri W=0 H=1 FPS=2 LEN=3 VCODEC=4 ACODEC=5 VDEC=6 CHANS=7 ASPECT=8 VCNAME=9 ACNAME=10 |
# Exit codes, same numbers as /usr/include/sysexits.h |
declare -r EX_OK=0 EX_USAGE=64 EX_UNAVAILABLE=69 \ |
EX_NOINPUT=66 EX_SOFTWARE=70 EX_CANTCREAT=73 \ |
EX_INTERRUPTED=79 # This one is not on sysexits.h |
# The context allows the creator to identify which contact sheet it is creating |
# (CTX_*) HL: Highlight (-l), STD: Normal, EXT: Extended (-e) |
declare -ri CTX_HL=1 CTX_STD=2 CTX_EXT=3 |
# Used for feedback |
declare -r NL=$'\012' # Newline |
#declare -r TAB=$'\011' # Tab |
# New in 1.13 |
# Set to 1 to disable blank frame evasion |
declare -i DISABLE_EVASION=0 |
# Threshold to consider a frame blank (see capture_and_evade) |
declare -i BLANK_THRESHOLD=10 |
# Offsets to try when trying to avoid blank frames |
# See capture() and capture_and_evade() |
declare -a EVASION_ALTERNATIVES=( -5 +5 -10 +10 -30 +30 ) |
# Save the terminal settings to later restore them (in exithdlr) |
declare -r STTY=$(stty -g) |
# }}} # End of constants |
# {{{ # Override-able variables |
# GETOPT must be correctly set or the script will fail. |
# It can be set in the configuration files if it isn't in the path or |
# the first getopt in the path isn't the right version. |
# A check will be made and a warning with details shown if required. |
declare GETOPT=getopt |
# Set to 1 to print function calls |
declare -i DEBUG=0 |
# Text before the user name in the signature |
declare SIGNATURE="Preview created by" |
# By default sign as the system's username (see -u, -U) |
declare USERNAME=$(id -un) |
# Which of the two methods should be used to guess the number of thumbnails |
declare -i TIMECODE_FROM=$TC_INTERVAL |
# New in 1.13. Replaces the old 'decoder' symbolic option. |
# The value is *not* the name of the executable, but a supported capturer, |
#+right now 'ffmpeg' or 'mplayer'. |
# When none is defined, the first available element in CAPTURERS is used. |
declare CAPTURER= |
# Options used in imagemagick, these options set the final aspect |
# of the contact sheet |
declare FORMAT=png # ImageMagick decides the type from the extension |
declare -i QUALITY=92 # Output image quality (only affects the final |
# image and obviously only in lossy formats) |
# Colours, see convert -list color to get the list |
declare BG_HEADING='#afcd7a' # Background for meta info (size, codec...) |
declare BG_SIGN=SlateGray #'#a2a9af' # Background for signature |
declare BG_TITLE=White # Background for the title (see -T) |
declare BG_CONTACT=White # Background for the captures |
declare BG_TSTAMPS='#000000aa' # Background for the timestamps box |
declare FG_HEADING=Black # Font colour for meta info box |
declare FG_SIGN=Black # Font colour for signature |
declare FG_TSTAMPS=White # Font colour for timestamps |
declare FG_TITLE=Black # Font colour for the title |
# Fonts, use identify -list font to get the list, up to IM 6.3.5-7 was '-list type' [[IM1]] |
# If a font is not available IM will pick a sane default. In theory it will be silent |
# although in practice it prints an error |
declare FONT_TSTAMPS=DejaVu-Sans-Book # Used for timestamps over the thumbnails |
declare FONT_HEADING=DejaVu-Sans-Book # Used for the meta info heading |
declare FONT_SIGN=$FONT_HEADING # Used for the signature box |
declare FONT_TITLE=$FONT_HEADING # Used for the title (see -T) |
# Font sizes, in points |
declare -i PTS_TSTAMPS=14 # Used for the timestamps |
declare -i PTS_META=14 # Used for the meta info heading |
declare -i PTS_SIGN=10 # Used for the signature |
declare -i PTS_TITLE=33 # Used for the title (see -T) |
# See -E / $END_OFFSET |
declare -r DEFAULT_END_OFFSET="5.5%" |
# Controls how many extra captures will be created in the extended mode |
# (see -e), 0 is the same as disabling the extended mode |
# This number is multiplied by the total number of captures to get |
# the number of extra captures. So, e.g. -n2 -e2 leads to 4 extra captures. |
declare EXTENDED_FACTOR=0 |
# Verbosity level so far from the command line can only be muted (see -q) |
# it can be overridden, though |
declare -i VERBOSITY=$V_INFO |
# Set to 1 to disable colours in console output |
declare -i SIMPLE_FEEDBACK=0 |
# See coherence_check for more details |
declare -i DISABLE_SHADOWS=0 |
declare -i DISABLE_TIMESTAMPS=0 |
# This font is used to display international names (i.e. CJK names) correctly |
# Help from users who actually need this would be appreciated :) |
# This variable is filled either automatically through the set_extended_font() |
#+function (and option -Ij) or manually (with option -Ij=MyFontName) |
# The automatic picks a semi-random one from the fonts believed to support CJK/Cyrillic |
#+characters. |
declare NONLATIN_FONT= # Filename or font name as known to ImageMagick (identify -list font) |
# Introduced in 1.12.2: |
# When true (1) uses $NONLATIN_FONT to print the filename, otherwise the same |
#+font as the heading is used. |
# See -I and --nonlatin |
declare -i NONLATIN_FILENAMES=0 |
# Output of capturing programs is redirected here |
declare STDOUT=/dev/null STDERR=/dev/null |
# Override-able since 1.11: |
# Height of the thumbnails, by default use same as input |
declare HEIGHT='100%' |
declare INTERVAL=$DEFAULT_INTERVAL # Interval of captures (~length/$NUMCAPS) |
declare -i NUMCAPS=16 # Number of captures (~length/$INTERVAL) |
# This is the padding added to each capture. |
# Beware when changing this since extended set's alignment might break. |
# When shadows are enabled this is ignored since they already add padding. |
declare -i PADDING=2 |
declare -i COLUMNS=2 # Number of output columns |
# This amount of time is *not* captured from the end of the video |
declare END_OFFSET=$DEFAULT_END_OFFSET |
# When set to 1 the signature won't contain the "Preview created by..." line |
declare -i ANONYMOUS_MODE=0 |
# Profile(s) to load by default |
declare PROFILES= |
# }}} # End of override-able variables |
# {{{ # Variables |
# Options and other internal usage variables, no need to mess with this! |
declare TITLE="" |
declare FROMTIME=0 # Starting second (see -f) |
declare TOTIME=-1 # Ending second (see -t) |
declare -a INITIAL_STAMPS # Manually added stamps (see -S) |
declare -i MANUAL_MODE=0 # if 1, only command line timestamps will be used |
declare ASPECT_RATIO=0 # If 0 no transformations done (see -a) |
# If -1 try to guess (see -A) |
declare -a TEMPSTUFF # Temporary files |
declare -a TIMECODES # Timestamps of the video captures |
declare -a HLTIMECODES # Timestamps of the highlights (see -l) |
declare VCSTEMPDIR= # Temporary directory, all temporary files go there |
# Identification output from ffmpeg and mplayer for the current video |
declare FFMPEG_CACHE= |
declare MPLAYER_CACHE= |
# This holds the parsed identification values, see also the Indexes in VID |
# (defined in the constants block) |
declare -a VID=( ) |
# These variables will hold the output of tput, used |
# to colourise feedback |
declare PREFIX_ERR= PREFIX_INF= PREFIX_WARN= PREFIX_DBG= SUFFIX_FBACK= |
# Workarounds: |
# Argument order in FFmpeg is important -ss before or after -i will make |
# the capture work or not depending on the file. See -Wo. |
# TODO: [x1]. |
# Admittedly the workaraound is abit obscure: those variables will be added to |
# the ffmpeg arguments, before and after -i, replacing spaces by the timestamp. |
# e.g.: for second 50 '-ss ' will become '-ss 50' while '' will stay empty |
# By default -ss goes before -i. |
declare wa_ss_af="" wa_ss_be="-ss " |
# Transformations/filters |
# Operations are decomposed into independent optional steps, this allows |
# to add some intermediate steps (e.g. polaroid/photo mode's frames) |
# Filters in this context are functions. |
# There're two kinds of filters and a delegate: |
# * individual filters are run over each vidcap |
# * global filters are run over all vidcaps at once (currently deprecated) |
# * The contact sheet creator delegates on some function to create the actual |
# contact sheet |
# |
# Individual filters take the form: |
# filt_name( vidcapfile, timestamp in seconds.milliseconds, width, height, [context, [index]] ) |
# They must set the variable $RESULT with parameters to add to 'convert', a single |
# call to convert will be issued for each capture like: |
# $ convert vidcap.png $RESULT [...] vidcap.png |
# They're executed in order by filter_vidcap() |
declare -a FILTERS_IND=( 'filt_resize' 'filt_apply_stamp' 'filt_softshadow' ) |
# Deprecated: Global filters take the form |
# filtall_name( vidcapfile1, vidcapfile2, ... ) |
# They're executed in order by filter_all_vidcaps |
declare -a FILTERS_CS |
# The contact sheet creators take the form |
# csheet_name( number of columns, context, width, height, vidcapfile1, |
# vidcapfile2, ... ) : outputfile |
# Context is one of the CTX_* constants (see below) |
# The width and height are those of an individual capture |
# It is executed by create_contact_sheet() |
declare CSHEET_DELEGATE=csheet_montage |
# Holds a list of captured frames (to avoid recapturing) |
# Format <timestamp>:<filename>[NL]<timestamp>:<filename>... |
declare CAPTURES= |
# Gravity of the timestamp |
declare GRAV_TIMESTAMP=SouthEast |
# Sets which function is used to obtain random numbers valid values are |
# bashrand and filerand. |
# Setting it manually will break it, calling with -R changes this to filerand. |
# See rand() for an explanation |
declare RANDFUNCTION=bashrand |
# Which file are we working on (i.e. how many times has process() been called) |
declare -i FILEIDX=0 |
# Names for output files, each index is a file name, an empty index will use |
# the input file and append an extension to it |
declare -a OUTPUT_FILES=( ) |
# Which of the two vidcappers should be used (see -F, -M) |
#+mplayer seems to fail for mpeg or WMV9 files, at least on my system |
#+also, ffmpeg allows better seeking: ffmpeg allows exact second.fraction |
#+seeking while mplayer apparently only seeks to nearest keyframe |
# Starting with 1.13 this value can no longer be overridden directly, |
#+setting 'decoder' actually changes CAPTURER. DECODER is still used |
#+internally. |
declare -i DECODER=$DEC_FFMPEG |
# Mplayer and FFmpeg binaries. Will be detected. |
# Don't set manually, if you need to override set the path temporarily, e.g.: |
# $ env PATH=/whatever:$PATH vcs ... |
# or use the undocumented (and unchecked!) appropriate option: |
# $ vcs --undocumented set_ffmpeg=/mypath/ffmpeg |
declare MPLAYER_BIN= |
declare FFMPEG_BIN= |
# When set to 1 the reported length by mplayer and ffmpeg won't be trusted |
# and will trigger some custom tests. |
# Enabled automatically on problematic files |
declare -i QUIRKS=0 |
# If the reported lengths differ by at least this much QUIRKS will be enabled |
declare QUIRKS_LEN_THRESHOLD=0.2 |
# When trying to determine the correct length, file will be probed each...: |
declare QUIRKS_LEN_STEP=0.5 # ~ 10 frames @ 20fps |
# Maximum number of seconds to "rewind" from reported length (after this |
# vcs surrenders but processing continues with a rewinded length) |
declare QUIRKS_MAX_REWIND=20 |
# Set when the console output will be in color. It doesn't control color! |
declare HAS_COLORS= |
declare -i multiple_input_files=0 |
# Internal counts, used only to adjust messages |
declare -i INTERNAL_WS_C=0 # -Ws count |
declare -i INTERNAL_WP_C=0 # -Wp count |
declare -i INTERNAL_MAXREWIND_REACHED=0 # More -Ws in the command-line won't help |
# Loaded profiles. |
# Not an array to ease seeking, each name is followed by an space: |
# Format: "profile1[SP]profile2[SP]"... |
declare INTERNAL_L_PROFILES= |
declare -r UNDFLAG_DISPLAY_COMMAND=eog # Command to run with -Z display |
# Stores the names of variables overridden from the command-line, |
#+see cmdline_override() and "--override" |
declare CMDLINE_OVERRIDES="" |
# Implicit error handling (see die()), obviously inspired by C's errno |
# and PHP's die(). Functions adapted to use them allow uses like: |
# some_function arg || die |
# which will exit with the appropriate exit code and print the error message |
# (Introduced in 1.12, still being retrofitted) |
declare -i ERROR_CODE=0 # Exit code associated with the last error |
declare ERROR_MSG= # Error message associated to the last error |
# Used to buffer feedback (see buffered()) |
declare BUFFER= |
# This is only used to exit when -DD is used |
declare -i DEBUGGED=0 # It will be 1 after using -DD |
# See post_getopt_hooks() |
# Format: Priority:Command[:Arguments] (lower priority run sooner) |
declare -a POST_GETOPT_HOOKS=( ) |
declare -i DVD_MODE=0 DVD_TITLE= |
declare -a DVD_TITLES=( ) # Titles for each input DVD, filled by --dvd-title |
declare DVD_MOUNTP= # Mountpoint for DVD, detected & reset for each DVD |
declare DVD_VTS= # VTS, detected & reset for each DVD |
# New in 1.13: Modularisation of video decoders and identifiers, to ease additions |
# There's two types of video tools supported: capturers and identifiers |
# A capturer is used to extract video frames |
# An identifier is used to extract video information |
# This abstraction provides an interface to allow easy addition of tools and |
#+to handle missing tools with more ease than before. Each tool has a set of |
#+associated functions, some of them optional that provide the same interface. |
# Capturer functions: |
# <name>_capture(in, ts, out): Capture the frame from 'in' at 'ts' to 'out' |
# <name>_dvd_capture(in, ts, out) [optional]: Same for DVDs |
# Identifier functions: |
# <name>_identify(f): Extract information from 'f', fill <NAME>_ID with it |
# also fills RESULT with the same values |
# <name>_probe(file, ts): Try reaching 'ts' (test for video length) |
# Supported capturers. In order of preference. |
# An associated <name>_capturer must be defined |
CAPTURERS=( ffmpeg mplayer ) |
# Supported identifiers. In order of preference |
# An associated <name>_identify must be defined |
# 'classic' is a combination of ffmpeg and mplayer |
IDENTIFIERS=( classic ffmpeg mplayer ) |
# Will be filled with the elements from CAPTURERS found on the system |
# Lookup is done with <name>_check_avail, an associated <NAME>_BIN is to be |
# defined there, i.e. mplayer_test_avail sets MPLAYER_BIN |
CAPTURERS_AVAIL=( ) |
# Like CAPTURERS_AVAIL, for IDENTIFIERS |
IDENTIFIERS_AVAIL=( ) |
# Same for IDENTIFIERS |
IDENTIFIER='' |
# If 1, the selected CAPTURER understands the use of milliseconds |
CAPTURER_HAS_MS=0 |
# This variable is used in functions to avoid running them in a subshell, i.e. |
# instead of |
# ret=$(myfunc) |
# such functions are used as |
# myfunc |
# ret=$RESULT |
# This way 'myfunc' has access to all variables and can modify them. |
# Every function that modifies RESULT should overwrite its value. |
RESULT='' |
# Set by init_filt_film: |
FILMSTRIP= # Filename of the sprocket-holes strip image |
FILMSTRIP_HOLE_HEIGHT= # Height of an individual hole |
# Set by -Z trace=<FILTER>, where <FILTER> is regex to reduce the trace |
# verbosity. Only function names that match it will be printed. |
# 'grep -p' will be used to match |
INTERNAL_TRACE_FILTER= |
INTERNAL_NO_TRACE=0 # When 1, tracing is disabled (used by -DD) |
# }}} # Variables |
# {{{ # Configuration handling |
# New override system: This variable maps configuration variables to actual |
#+variables used in the script. Each item in the array follows the syntax: |
# <cfg variable>:<variable>:<flags>:[type constraints] Where: |
#+ cfg variable: is the name of the configuration file variable |
#+ variable: is the name of the actual variable. If empty or '=', it will be |
#+ the same as cfg variable. |
#+ flags can currently be: |
#+ "deprecated=new name": Will print a deprecation warning and suggest to use |
#+ "new name" instead |
#+ "striked": Variable is marked for removal, will print a warning about it |
#+ directing anyone needing it to contact me. Only used for variables |
#+ believed to be no longer needed |
#+ "gone": Variable removed in the current version |
#+ "alias": Marks an alias, duplicate name intended to stay |
#+ "meta": Special variable that will modify other variables (e.g. font_all |
#+ modifies all font_ variables. |
#+ "=": ignore |
#+ type constraints: a character indicating accepted values: |
# n -> Number (Natural, positive Integer or zero) |
# p -> Number, not zero |
# t -> Timestamp |
# b -> Bool |
# h -> Positive, non-zero, number or percentage |
# f -> Float or fraction |
# D -> only $DEC_* constants |
# T -> only $TC_* constants |
# V -> only $V_* constants |
# I -> interval or percentage |
# x -> Special, variable with a set of possible values |
# Note during the switch to the new system most variables will remain unchanged |
# Also, the new system is case insensitive to variable names |
declare -ra OVERRIDE_MAP=( |
"USER:USERNAME::" |
"EXTENDED_FACTOR:=:=:f" |
"STDOUT::" |
"STDERR::" |
"DEBUG:=:=:b" |
"INTERVAL:=:=:t" |
"NUMCAPS:=:=:p" |
"CAPTURES:NUMCAPS:alias:n" # Alias |
"GETOPT::" # Note it makes no sense as command-line override |
"COLUMNS:=:=:p" |
"COLS:COLUMNS:alias:p" # Traditional name |
"DISABLE_SHADOWS:=:=:b" |
"DISABLE_TIMESTAMPS:=:=:b" |
"BG_HEADING::" |
"BG_SIGN::" |
"BG_TITLE::" |
"BG_CONTACT::" |
"BG_TSTAMPS::" |
"FG_HEADING::" |
"FG_SIGN::" |
"FG_TSTAMPS::" |
"FG_TITLE::" |
"FONT_HEADING::" |
"FONT_SIGN::" |
"FONT_TSTAMPS::" |
"FONT_TITLE::" |
"FONT_ALL:=:meta" # see parse_override |
"BG_ALL:=:meta" |
"FG_ALL:=:meta" |
"PTS_TSTAMPS::" |
"PTS_META::" |
"PTS_SIGN::" |
"PTS_TITLE::" |
# Aliases for cosmetic stuff |
"BG_HEADER:BG_HEADING:alias" |
"BG_SIGNATURE:BG_SIGN:alias" |
"BG_FOOTER:BG_SIGN:alias" |
"BG_SHEET:BG_CONTACT:alias" |
"FG_HEADER:FG_HEADING:alias" |
"FG_SIGNATURE:FG_SIGN:alias" |
"FG_FOOTER:FG_SIGN:alias" |
"FONT_HEADER:FONT_HEADING:alias" |
"FONT_META:FONT_HEADING:alias" |
"FONT_SIGNATURE:FONT_SIGN:alias" |
"FONT_FOOTER:FONT_SIGN:alias" |
"PTS_HEADING:PTS_META:alias" |
"PTS_HEADER:PTS_META:alias" |
"PTS_SIGNATURE:PTS_SIGN:alias" |
"PTS_FOOTER:PTS_SIGN:alias" |
"SIGNATURE:=:" |
"USER_SIGNATURE:SIGNATURE:deprecated=SIGNATURE" # Deprecated since 1.12 |
"QUALITY:=:=:n" |
"OUTPUT_QUALITY:QUALITY:deprecated=QUALITY:n" # Deprecated since 1.12 |
# TODO: These variables are evaluated to constants, would be better to |
# use some symbolic system (e.g. decoder=f instead of decoder=$DEC_FFMPEG) |
"DECODER:=:meta:D" # To be deprecated |
#"CAPTURE_MODE:TIMECODE_FROM:alias:T" |
"TIMECODE_FROM:=:=:T" |
"VERBOSITY:=:=:V" |
"SIMPLE_FEEDBACK:=:=:b" |
"CAPTURER:=:=:x" # Setting this modifies DECODER and CAPTURER_HAS_MS, from pick_tools() |
"HEIGHT:=:=:h" |
"PADDING:=:=:n" |
"NONLATIN_FONT::" |
"NONLATIN_FILENAMES:=:=:b" |
"ANONYMOUS:ANONYMOUS_MODE:=:b" |
"FORMAT::" |
"END_OFFSET:=:=:I" # New, used to have a two-variables assignment before USR_* |
"PROFILES:=:meta:P" # New in 1.13 |
# TODO TBA: |
#"noboldfeedback::" # Colour but not bold |
# Deprecations, all these since 1.12 |
"OUTPUT_FORMAT:FORMAT:deprecated=FORMAT" |
"PLAIN_MESSAGES:SIMPLE_FEEDBACK:deprecated=SIMPLE_FEEDBACK:b" |
"TH_HEIGHT:HEIGHT:deprecated=HEIGHT:h" |
"HPAD:PADDING:deprecated=PADDING:n" |
"FONT_MINCHO:NONLATIN_FONT:deprecated=NONLATIN_FONT" |
# Gone. Since 1.12 |
"MIN_LENGTH_FOR_END_OFFSET::gone:" |
# Gone. Since 1.13 |
"SHOEHORNED::gone" |
"SAFE_RENAME_PATTERN::gone" |
"DEFAULT_END_OFFSET::gone:" |
) |
# Load a configuration file |
# File *MUST* exist |
# Configuration files are a series of variable=value assignment; they'll be |
#+evaluated directly so they can refer to other variables (with their value at |
#+the point of the assignment). |
# Quotes shouldn't be used (they'll be kept) |
# Since 1.12 comments can be placed in-line (i.e. after an assignment), |
# Literal '#' can be written as '$#' |
# ';' can be used to mark an end of line, anything after it will be ignored |
#+(making it equivalent to '#'), there's no way to include a literal ';' |
# load_config_file($1 = file, [$2 = type (description) = 'Settings']) |
load_config_file() { |
trace $@ |
local cfgfile=$1 |
local desc=$2 |
[[ $desc ]] || desc='Settings' |
local por= # Parsed override |
local varname= tmp= flag= bashcode= feedback= ov= |
while read line ; do # auto variable $line |
[[ ! $line =~ ^[[:space:]]*# ]] || continue # Don't feed comments |
parse_override "$line" |
por=$RESULT |
if [[ $por ]]; then |
varname=${por/% *} # Everything up to the first space... |
tmp=${por#* } # Rest of string |
flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
# No need to override... |
feedback="$varname(=)" |
else |
feedback=$varname |
fi |
ov="$ov, $feedback" |
fi |
done <$cfgfile |
[[ -z $ov ]] || inf "$desc from $cfgfile:$NL ${ov:2}" |
# No loaded overrides but errors/warnings to print, do print the file name |
if [[ ( -z $ov ) && $BUFFER ]]; then |
inf "In $cfgfile:" |
fi |
flush_buffered ' ' |
} |
# Loads the configuration files if present |
# load_config() |
load_config() { |
local -a CONFIGS=( /etc/vcs.conf ~/.vcs.conf ~/.vcs/vcs.conf ./vcs.conf ) |
for cfgfile in "${CONFIGS[@]}" ;do |
[[ -f $cfgfile ]] || continue |
load_config_file "$cfgfile" |
done |
if [[ -f "./vcs.conf" ]]; then |
warn "'./vcs.conf' won't be loaded automatically starting with vcs 1.14" |
warn " use '-C :pwd' to manually load it, or convert it to a profile" |
fi |
} |
# Load a profile, if found; fail otherwise |
# Profiles are just configuration files that can be loaded on demand (whereas |
#+config files are always loaded) and be given a name. |
# See load_config_file() for comments on the syntax |
# Locations to be searched, in order: |
#+ 1) ~/.vcs/profiles/NAME.conf |
#+ 2) /usr/local/share/vcs/profiles/NAME.conf |
#+ 3) /usr/share/vcs/profiles/NAME.conf |
#+i.e. files in ~/.vcs/ will prevent loading files named like them in /usr |
# load_profile($1 = profile name) |
load_profile() { |
trace $@ |
local p=$1 prof= |
local -a PATHS=( ~/.vcs/profiles/ /usr/local/share/vcs/profiles/ /usr/share/vcs/profiles/ ) |
if [[ ${p:0:1} == ':' ]]; then |
case $p in |
:list) |
echo "Builtin profiles:" |
echo ' * classic: Classic colour scheme from previous versions' |
echo ' * 1.0: Initial colour scheme from ancient versions' |
# No need to be efficient here... |
echo "Profiles located:" |
local path= profname= |
# 1) Find all profiles |
# 2) (sed) Extract profile file name |
# 3 & 4) (sort+uniq) Keep only first hits for each name (most precedence) |
# 5) (while) Process each name |
# 6) (for) Re-locate most precedent profile |
# 7) (echo x3) Print <name>[: description] |
# 8) (sed) Indent with ' * ' |
find "${PATHS[@]}" -name '*.conf' 2>/dev/null \ |
| sed -e 's#.*/\(.*\)\.conf#\1#' \ |
| sort | uniq \ |
| while read profname ; do |
for path in "${PATHS[@]}" ; do |
path=$path$profname.conf |
[[ -f $path ]] || continue |
echo -n "$profname" |
# [ ] here contains <space><tab>. Mawk doesn't understand |
# [[:space:]] |
echo -n $(awk 'sub(/#[ ]*vcs:desc:[ ]*/, ": ")' "$path") |
echo |
break |
done |
done \ |
| sed 's/^/ * /' |
exit 0 |
;; |
*) |
ERROR_MSG="Profiles starting with ':' are reserved.$NL" |
ERROR_MSG+=" Use ':list' to list available profiles." |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
esac |
fi |
for prof in "${PATHS[@]}" ; do |
prof="$prof$p.conf" |
[[ -f $prof ]] || continue |
INTERNAL_L_PROFILES+="$p " |
load_config_file "$prof" 'Profile' |
return 0 |
done |
ERROR_MSG="Profile '$p' not found" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
} |
# Check value for an overrideable variable against the allowed values |
# check_constraint($1 = variable name, $2 = value [, $3 = public_name]) |
# where public_name is the name to be used for error messages |
check_constraint() { |
trace $@ |
local n=$1 v=$2 p=$3 |
# Get constraint... |
local needle=$n |
# ... use the public name to search UNLESS it is a command-line option |
if [[ ( -n $p ) && ! ( $p =~ ^- ) ]]; then |
needle=$p |
fi |
local map=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$needle:") |
[[ $map ]] || return 0 |
local ct=$(cut -d':' -f4 <<<"$map") |
[[ $ct ]] || return 0 |
local checkfn= domain= |
case $ct in |
n) checkfn=is_number ; domain=numbers ;; |
p) checkfn=is_positive ; domain='numbers greater than zero' ;; |
t) checkfn=is_interval ; domain=intervals ;; |
b) checkfn=is_bool ; domain='boolean values (0 or 1)' ;; |
h) checkfn=is_pos_or_percent ; domain='positive numbers or percentages' ;; |
f) checkfn=is_float_or_frac ; domain='positive numbers or fractions' ;; |
D) checkfn=is_decoder ; domain='$DEC_FFMPEG or $DEC_MPLAYER' ;; |
T) checkfn=is_tcfrom ; domain='$TC_INTERVAL or $TC_INTERVAL' ;; |
V) checkfn=is_vlevel ; domain='verbosity levels ($V_.*)' ;; |
I) checkfn=is_interv_or_percent ; domain='intervals or percentages' ;; |
P) checkfn=is_profile_list ; domain='comma-separated profile names' ;; |
x) |
case "$p" in |
capturer) |
checkfn=is_known_capturer |
domain='mplayer or ffmpeg' |
;; |
esac |
esac |
if [[ -n $checkfn ]] && ! $checkfn "$v" ; then |
[[ -n $p ]] || p=$n |
ERROR_MSG="Illegal value for '$p', only $domain are accepted" |
ERROR_CODE=$EX_USAGE |
return $ERROR_CODE |
fi |
return 0 |
} |
# Parse an override and set its value. |
# Input should be a var=value assignment. Also sets USR_<variable>. |
# The global variable $RESULT is set with the format: |
# <variable name> <flag> where |
# * variable name: is the name of the variable to be overridden |
# * flag: is a character indicating the status: "+" for a possible override, |
# "=" for an override that already has the same value |
# Warnings and errors are buffered |
# This function always returns true |
# parse_override($1 = override assignment) |
parse_override() { |
trace $@ |
local o="$1" |
RESULT='' |
# bash 3.1 and 3.2 handle quoted eres differently, using a variable fixes this |
local ERE="^[[:space:]]*[[:alpha:]_][[:alnum:]_]*[[:space:]]*=.*" |
if [[ ! $o =~ $ERE ]] ; then |
return |
fi |
local varname=$(echo "${o/=*}" | sed 's/[[:space:]]//g') # Trim var name |
local lcvarname=$(echo "$varname" | tr A-Z a-z) |
local mapping=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$lcvarname:") |
[[ $mapping ]] || return 0 |
local varval=${o#*=} # No trimming here (yet) |
# 1) Trim from ; (if present) to finish |
# 2) Trim from # (comments) not "escaped" like '$#' |
# 3) Replace '$#' with '#' |
# 4) Trim whitespace on both ends |
varval=$(sed -e 's/;.*//' -e 's/\([^$]\)#.*/\1/g' -e 's/\$#/#/g' \ |
-e 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$varval") |
# Is varval empty? |
[[ $varval ]] || return 0 |
local mvar=$(cut -d':' -f1 <<<"$mapping") |
local ivar=$(cut -d':' -f2 <<<"$mapping") |
local flags=$(cut -d':' -f3 <<<"$mapping") |
local constraints=$(cut -d':' -f4 <<<"$mapping") |
{ [[ $ivar && ( $ivar != '=' ) ]] ; } || ivar="$mvar" |
# Evaluate setting names, unlike actual variables they are |
#+case-insensitive and can mapped to different names so |
#+special handling is required |
local token= tokenmap= |
for token in $(echo "$varval" | grep -o '\$[[:alnum:]_]*' | sed 's/^\$//') ; do |
# Locate the mapping |
tokenmap=$(echo "${OVERRIDE_MAP[*]}" | stonl | egrep -i "^$token") || true |
if [[ -z $tokenmap ]]; then |
# No mapping, leave intact |
continue |
fi |
tokenmap=$(echo "$tokenmap" | cut -d':' -f2) |
if [[ -z $tokenmap ]]; then |
# No need to map, but change to uppercase for it to eval correctly |
tokenmap=$(tr a-z A-Z <<<"$token") |
fi |
# Replace all occurences of $token with its mapping |
varval=$(echo "$varval" | sed 's/\$'$token'/$'$tokenmap'/g') |
done |
# Note using "\$(echo $varval)" would allow a more flexible syntax but |
#+enforce special handling of escaping, which with the currently available |
#+settings is not worth the effort |
# Resolve symbolic variables to check their actual value |
eval varval="\"$varval\"" 2>/dev/null || { # Hide eval's errors |
buffered error "Syntax error: '$o'" |
return 0 |
} |
[[ $varval ]] || return 0 # If empty value, ignore it |
local evcode='' |
if [[ $flags && ( $flags != '=' ) && ( $flags != 'alias' ) ]]; then |
local ERE='^deprecated=' |
if [[ $flags =~ $ERE ]]; then |
local new=$(echo "$flags" | sed 's/^deprecated=//' | tr A-Z a-z) |
buffered warn "Setting '$varname' will be removed in the future,$NL please use '$new' instead." |
else |
case "$flags" in |
gone) |
buffered error "Setting '$varname' has been removed." |
return 0 |
;; |
striked) |
buffered error "Setting '$varname' is scheduled to be removed in the next release." |
buffered error " Please contact the author if you absolutely need it." |
;; |
meta) |
if [[ -n $constraints ]] ; then |
if ! check_constraint $ivar "$varval" $varname ; then |
buffered error "$ERROR_MSG" |
return 0 |
fi |
fi |
apply_meta_override "$varname" "$varval" |
RESULT="$varname +" |
return 0; |
;; |
*) return 0 ;; |
esac |
fi |
fi |
[[ -z $constraints ]] || check_constraint $ivar "$varval" $varname || { |
buffered error "$ERROR_MSG" |
return 0 |
} |
eval local curvarval='$'"$ivar" retflag='+' |
if [[ $constraints == 't' ]]; then |
varval=$(get_interval "$varval") |
fi |
# Escape single quotes, since it will be single-quoted: |
varval=${varval//\'/\'\\\'\'} # <<'>> => <<'\''>> |
evcode="USR_$ivar='$varval'" |
if [[ $curvarval == "$varval" ]]; then |
retflag='=' |
else |
evcode="$ivar='$varval'; $evcode" |
fi |
eval "$evcode" |
# varname, as found in the config file |
RESULT="$varname $retflag" |
} |
# Handle meta configuration variables, variables that, when set, modify the |
# value of (various) others |
# apply_meta_override($1 = actual variable name, $2 = value) |
apply_meta_override() { |
trace $@ |
case "$(tolower "$1")" in |
font_all) |
buffered inf "font_all => font_heading, font_sign, font_title, font_tstamps" |
parse_override "FONT_HEADING=$2" |
parse_override "FONT_SIGN=$2" |
parse_override "FONT_TITLE=$2" |
parse_override "FONT_TSTAMPS=$2" |
;; |
fg_all) |
buffered inf "fg_all => fg_heading, fg_sign, fg_title, fg_tstamps" |
parse_override "FG_HEADING=$2" |
parse_override "FG_SIGN=$2" |
parse_override "FG_TSTAMPS=$2" |
parse_override "FG_TITLE=$2" |
;; |
bg_all) |
buffered inf "bg_all => bg_heading, bg_contact, bg_sign, bg_title, bg_tstamps" |
parse_override "BG_HEADING=$2" |
parse_override "BG_CONTACT=$2" |
parse_override "BG_SIGN=$2" |
parse_override "BG_TITLE=$2" |
parse_override "BG_TSTAMPS=$2" |
;; |
profiles) # profiles=[,]prof1[,prof2,...], no spaces |
local profiles=${2//,/ } # === sed 's/,/ /g' |
local ERE='^[[:space:]]*$' |
if [[ $profiles =~ $ERE ]]; then |
return 0 |
fi |
local prof= |
for prof in ${2//,/ } ; do # ${2//,/ } = sed 's/,/ /g' |
grep -q -v "$prof " <<<"$INTERNAL_L_PROFILES" || continue |
load_profile $prof || die |
done |
;; |
decoder) |
buffered inf "decoder => capturer" |
if [[ $2 -eq $DEC_FFMPEG ]]; then |
parse_override 'CAPTURER=ffmpeg' |
elif [[ $2 -eq $DEC_MPLAYER ]]; then |
parse_override 'CAPTURER=mplayer' |
else |
assert false |
fi |
;; |
esac |
} |
# Do an override from the command line |
# cmdline_override($1 = override assignment) |
#+e.g. cmdline_override 'verbosity=$V_ALL' |
cmdline_override() { |
trace $@ |
parse_override "$1" |
local r=$RESULT |
[[ $r ]] || return 0 |
local varname=${r/% *} # See load_config() |
local tmp=${r#* } |
local flag=${tmp/% *} |
if [[ $flag == '=' ]]; then |
varname="$varname(=)" |
fi |
CMDLINE_OVERRIDES="$CMDLINE_OVERRIDES, $varname" |
} |
# Call any pending commands required by the command-line arguments |
# This is used to defer some calls and to flush buffers |
post_getopt_hooks() { |
local cback= EX=0 |
local funcs=$(echo "${POST_GETOPT_HOOKS[*]}" | stonl | sort -n | uniq |\ |
cut -d':' -f2- ) |
for cback in $funcs ; do |
local fn=${cback/:*} |
local arg=${cback/*:} |
[[ $arg != $cback ]] || arg='' |
$fn $arg |
done |
} |
# Print the list of command-line overrides |
cmdline_overrides_flush() { |
trace $@ |
if [[ $CMDLINE_OVERRIDES ]]; then |
inf "Overridden settings from command line:$NL ${CMDLINE_OVERRIDES:2}" |
fi |
if [[ $BUFFER ]]; then |
[[ $CMDLINE_OVERRIDES ]] || warn "In command-line overrides:" |
flush_buffered ' ' |
fi |
} |
# }}} # Configuration handling |
# {{{ # Convenience functions |
#### {{{{ # Type checkers: Return true if input is of a certain type |
#### All take exactly one argument and print nothing |
## Natural number |
is_number() { |
# With '[[...]]', strings '-eq'uals 0, test if it's actually 0 |
#+or otherwise a valid number. Must return 1 on error. |
[[ ( $1 == '0' ) || ( $1 -gt 0 ) ]] 2>/dev/null || return 1 |
} |
## Number > 0 |
is_positive() { is_number "$1" && [[ $1 -gt 0 ]]; } |
## Bool (0 or 1) |
is_bool() { [[ ($1 == '0') || ($1 == '1') ]] 2>/dev/null ; } |
## Float (XX.YY; XX.; ;.YY) (.24=0.24) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))$' |
is_float() { local P='^([0-9]+\.?[0-9]*|\.[0-9]+)$' ; [[ $1 =~ $P ]] ; } |
## Percentage (xx% or xx.yy%) |
## XXX: 1.12.3: '^([0-9]+\.?([0-9])?+|(\.[0-9]+))%$' |
is_percentage() { |
local P='^([0-9]+\.?[0-9]*|\.[0-9]+)%$' |
[[ $1 =~ $P ]] |
} |
## Interval |
is_interval() { |
local i=$(get_interval "$1" || true) |
[[ $i ]] && fptest $i -gt 0 |
} |
## Interval or percentage |
is_interv_or_percent() { |
is_percentage "$1" || is_interval "$1" |
} |
## Positive or percentage |
is_pos_or_percent() { |
is_number "$1" && [[ $1 -gt 0 ]] || is_percentage "$1" |
} |
## Float (>=0) or fraction |
is_float_or_frac() { |
{ is_fraction "$1" || is_float "$1" ; } && fptest "$1" -ge 0 |
} |
## Fraction, strictly (X/Y, but no X; Y!=0) |
is_fraction() { |
local P='^[0-9]+/[0-9]+$' |
[[ $1 =~ $P ]] && { |
local d=${1#*/} # .../X |
[[ $d -ne 0 ]] |
} |
} |
## Decoder ($DEC_* constants) |
is_decoder() { [[ $1 == $DEC_FFMPEG || $1 == $DEC_MPLAYER ]]; } |
is_known_capturer() { |
[[ ( $1 == 'mplayer' ) || ( $1 == 'ffmpeg' ) ]] |
} |
## Time calculation source ($TC_* constants) |
is_tcfrom() { [[ $1 == $TC_INTERVAL || $1 == $TC_NUMCAPS ]]; } |
## Verbosity level ($V_* constants) |
is_vlevel() { |
is_number "$1" && \ |
[[ ($1 -eq $V_ALL) || ($1 -eq $V_NONE) || ($1 -eq $V_ERROR) || \ |
($1 -eq $V_WARN) || ($1 -eq $V_INFO) ]] |
} |
## List of profiles (comma-separated) |
is_profile_list() { |
ERE='^([[:alnum:]]*,?)*$' |
[[ ( -z "$*" ) || ( "$*" =~ $ERE ) ]] |
} |
#### }}}} # End of type checkers |
# Makes a string lowercase |
# tolower($1 = string) |
tolower() { tr '[:upper:]' '[:lower:]' <<<"$1" ; } |
# Rounded product |
# multiplies parameters and prints the result, rounded to the closest int |
# parameters can be separated by commas or spaces |
# e.g.: rmultiply 4/3,576 OR 4/3 576 = 4/3 * 576 = 768 |
# rmultiply($1 = operator1, [$2 = operator2, ...]) |
# rmultiply($1 = "operator1,operator2,...") |
rmultiply() { |
awkex "int(${*//[ ,]/ * }+0.5)" # ' ' = ',' => '*' |
} |
# Like rmultiply() but always rounded upwards |
ceilmultiply() { |
# TODO: breaks with $@. Why? |
awkex "int(${*//[ ,]/ * }+0.99999)" # ' ' = ',' => '*' |
} |
# Basic mathematic stuff |
# min($1 = operand1, $2 = operand2) |
# max($1 = operand1, $2 = operand2) |
# abs($1 = number) |
min() { awk "BEGIN { if (($1) < ($2)) print ($1) ; else print ($2) }" ; } |
max() { awk "BEGIN { if (($1) > ($2)) print ($1) ; else print ($2) }" ; } |
abs() { awk "BEGIN { if (($1) < (0)) print (($1) * -1) ; else print ($1) }" ; } |
# Rounds a number ($1) to a multiple of ($2) |
# rtomult($1 = number, $2 = divisor) |
rtomult() { |
local n=$1 d=$2 |
local r=$(( $n % $d )) |
if [[ $r -ne 0 ]]; then |
(( n += ( d - r ) , 1 )) |
fi |
echo $n |
} |
# Numeric test eqivalent for floating point |
# fptest($1 = op1, $2 = operator, $3 = op2) |
# special operator: '~' uses fsimeq() |
fptest() { |
local op= |
# Empty operands |
if [[ ( -z $1 ) || ( -z $3 ) ]]; then |
assert "[[ \"'$1'\" && \"'$3'\" ]] && false" |
fi |
case $2 in |
-gt) op='>' ;; |
-lt) op='<' ;; |
-ge) op='>=' ;; |
-le) op='<=' ;; |
-eq) op='==' ;; |
-ne) op='!=' ;; |
~) |
fsimeq "$1" "$3" |
return $? |
;; |
*) assert "[[ \"'$1' '$2' '$3'\" ]] && false" && return $EX_SOFTWARE |
esac |
awk "BEGIN { if ($1 $op $3) exit 0 ; else exit 1 }" |
} |
# floating point fuzzy equality, like fptest |
# fsimeq($1 = op1, $2 = op2) |
fsimeq() { |
awk "BEGIN { if (($1 - $2)^2 < 0.000000001) exit 0 ; else exit 1 }" |
} |
# Keep a number of decimals *rounded* |
# keepdecimals($1 = num, $2 = number of decimals) |
keepdecimals() { |
local N=$1 D=$2 |
awk "BEGIN { printf \"%.${D}f\", (($N)+0) }" |
} |
# Keep a number of decimals, last decimal rounded to lower |
keepdecimals_lower() { |
local ERE='\.' |
[[ $1 =~ $ERE ]] || { echo "$1" ; return ; } |
local D=${1/#*.} # Decimals only |
echo ${1/%.*}.${D:0:$2} # Integer part + . + Number of decimals |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Keep decimals. I.e. 5 = 5.000000... |
# awkexf($1 = expression) |
awkexf() { |
# By default awk prints in compact form (scientific notation and/or up to 6 digits/decimals), |
# printf is used to avoid this, TODO: Is there any direct way? |
# .%20f is clearly overkill but matches the old code (default bc -l) |
# TODO: gawk and mawk differ in how to handle stuff like div by zero: |
# gawk errors, mawk prints inf. Should somehow handle inf and nan |
awk "BEGIN { printf \"%.20f\", ($1)+0 }" |
} |
# Evaluate in AWK. Intended for arithmetic operations. |
#+Use default output. I.e. 5 = 5 |
# awkex($1 = expression) |
awkex() { |
awk "BEGIN { print ($1)+0 }" |
} |
# converts spaces to newlines in a x-platform way [[FNL]] |
# stonl([$1 = string]) |
stonl() { |
if [[ $1 ]]; then |
awk '{gsub(" ", "\n");print}' <<<"$1" | egrep -v '^$' |
else |
awk '{gsub(" ", "\n");print}' | egrep -v '^$' |
fi |
} |
# Converts newlines to spaces portably |
# nltos([$1 = string]) |
nltos() { |
if [[ $1 ]]; then |
awk '{printf "%s ",$0}' <<<"$1" | sed 's/ *//' |
else |
awk '{printf "%s ",$0}' | sed 's/ *//' |
fi |
} |
# bash version of ord() [[ORD]] |
# prints the ASCII value of a character |
ord() { |
printf '%d' "'$1" |
} |
# Get file extension |
filext() { |
grep -q '\.' <<<"$1" || return 0 |
awk -F. '{print $NF}' <<<"$1" |
} |
# Checks if a 'command' is defined either as an available binary, a function |
#+or an alias |
# is_defined($1 = command) |
is_defined() { |
type "$@" >/dev/null 2>&1 |
} |
# Checks if a command is an available binary in the path. |
# is_executable($1 = command) |
is_executable() { |
type -pf "$@" >/dev/null 2>&1 |
} |
# Checks if a variable has been defined (even to empty values). |
# isset($1 = variable name) |
isset() { |
[[ -n ${!1+x} ]] |
} |
# Wrapper around $RANDOM, not called directly, wrapped again in rand(). |
# See rand() for an explanation. |
bashrand() { |
echo $RANDOM |
} |
# Prepares for "filerand()" calls |
# File descriptor 7 is used to keep a file open, from which data is read |
# and then transformed into a number. |
# init_filerand($1 = filename) |
init_filerand() { # [[FD1]], [[FD2]] |
test -r "$1" |
exec 7<"$1" |
# closed in exithdlr |
} |
# Produce a (not-really-)random number from a file, not called directly wrapped |
# in rand() |
# Note that once the file end is reached, the random values will always |
# be the same (hash_string result for an empty string) |
filerand() { |
local b= |
# "read 5 bytes from file descriptor 7 and put them in $b" |
read -n5 -u7 b |
hash_string "$b" |
} |
# Produce a random number |
# $RANDFUNCTION defines wich one to use (bashrand or filerand). |
# Since functions using random values are most often run in subshells |
# setting $RANDOM to a given seed has not the desired effect. |
# filerand() is used to that effect; it keeps a file open from which bytes |
# are read and not-so-random values generated; since file descriptors are |
# inherited, subshells will "advance" the random sequence. |
# Argument -R enables the filerand() function |
rand() { |
$RANDFUNCTION |
} |
# produces a numeric value from a string |
hash_string() { |
local HASH_LIMIT=65536 |
local v=$1 |
local -i hv=15031 |
local c= |
if [[ $v ]]; then |
for i in $(seqr 0 ${#v} ); do |
c=$( ord ${v:$i:1} ) |
hv=$(( ( ( $hv << 1 ) + $c ) % $HASH_LIMIT )) |
done |
fi |
echo $hv |
} |
# Applies the Pythagorean Theorem |
# pyth_th($1 = cathetus1, $2 = cathetus2) |
pyth_th() { |
awkexf "sqrt($1 ^ 2 + $2 ^ 2)" |
} |
# Get a percentage |
# percent($1 = value, $2 = percentage) |
percent() { |
local pc=${2/%%/} # BASH %% == RE %$ |
awkexf "($1 * $pc) / 100" |
} |
# Rounded percentage |
# rpercent($1 = value, $2 = percentage) |
rpercent() { |
local pc=${2/%%/} |
awkex "int( ($1 * $pc) / 100 + 0.5 )" |
} |
# Prints the width correspoding to the input height and the variable |
# aspect ratio |
# compute_width($1 = height) (=AR*height) (rounded) |
compute_width() { |
rmultiply $ASPECT_RATIO,$1 |
} |
# Parse an interval and print the corresponding value in seconds |
# returns something not 0 if the interval is not recognized. |
# |
# The current code is a tad permissive, it allows e.g. things like |
# 10m1h (equivalent to 1h10m) |
# 1m1m (equivalent to 2m) |
# I don't see reason to make it more anal, though. |
# get_interval($1 = interval) |
get_interval() { |
trace $@ |
# eval it even if it's numeric to strip leading zeroes. Note the quoting |
if is_number "$1" ; then awkexf "\"$1\"" ; return 0 ; fi |
local s=$(tolower "$1") r |
# Only allowed characters |
local ERE='^[0-9smhSMH.]+$' |
[[ $s =~ $ERE ]] || return $EX_USAGE |
# Two consecutive dots are no longer accepted |
# ([.] required for bash 3.1 + bash 3.2 compat) |
[[ ! $s =~ [.][.] ]] || return $EX_USAGE |
# Newer(-er) parsing code: replaces units by a product |
# and feeds the resulting string to awk for evaluation |
# Note leading zeroes will lead awk to believe they are octal numbers |
# as a quick and dirty fix I'm just wrapping them in quotes, forcing awk |
# to re-evaluate them, which appears to be enough to make them decimal. |
# This is the only place where leading zeroes have no meaning. |
# sed expressions: |
# 1: add spaces after h,m,s and before '.' |
# 2: add a space at the start (every number will now have a space in front) |
# 3: quote numbers preceded by a space |
# 4: replace h with a product by 3600 and an addition |
# 5: replace m with a product by 60 and an addition |
# 6: replace s with an addition |
# 7: add a '+' between consecutive quoted values |
# 8: remove last empty addition |
local exp=$(echo "$s" | sed \ |
-e 's/\([hms]\)/\1 /g' -e 's/\./ ./g' \ |
-e 's/^/ /' \ |
-e 's/ \([0-9.][0-9.]*\)/ "\1"/g' \ |
-e 's/h/ * 3600 + /g' \ |
-e 's/m/ * 60 + /g' \ |
-e 's/s/ + /g' \ |
-e 's/"[[:space:]]*"/" + "/g' \ |
-e 's/+ *$//' \ |
) |
r=$(awkexf "$exp" 2>/dev/null) |
# Negative and empty intervals |
assert "[[ '$r' ]]" |
assert "fptest $r -gt 0" |
echo $r |
} |
# Pads a string with zeroes on the left until it is at least |
# the indicated length |
# pad($1 = minimum length, $2 = string) |
pad() { |
# Must allow non-numbers |
local l; (( l = $1 - ${#2} , 1 )) |
[[ $l -le 0 ]] || printf "%0${l}d" '0' |
echo $2 |
} |
# Get Image Width |
# imw($1 = file) |
imw() { |
identify -format '%w' "$1" |
} |
# Get Image Height |
# imh($1 = file) |
imh() { |
identify -format '%h' "$1" |
} |
# Get the line height used for a certain font and size |
# line_height($1 = font, $2 = size) |
line_height() { |
# Create a small image to see how tall are characters. In my tests, no |
#+matter which character is used it's always the same height. |
convert -font "$1" -pointsize "$2" \ |
label:'F' png:- | identify -format '%h' - |
} |
# Prints a number of seconds in a more human readable form |
# e.g.: 3600 becomes 1:00:00 |
# pretty_stamp($1 = seconds) |
pretty_stamp() { |
assert "is_float '$1'" |
assert 'isset CAPTURER_HAS_MS' |
# Fully implemented in AWK to discard bc. |
# As a bonus now it's much faster and compact |
awk "BEGIN { |
t=$1 ; NOTMS=!$CAPTURER_HAS_MS; |
MS=(t - int(t)); |
h=int(t / 3600); |
t=(t % 3600); |
m=int(t / 60); |
t=(t % 60); |
s=t |
if (h != 0) h=h\":\" ; else h=\"\" |
if (NOTMS!=1) ms=sprintf(\".%02d\", int(MS*100+0.5)); |
printf \"%s%02d:%02d%s\", h, m, s, ms |
}" |
# Note the rounding applied to $MS, it is required to match the precission passed on |
# to ffmpeg |
} |
# Prints a given size in human friendly form |
get_pretty_size() { |
local bytes=$1 |
local size= |
if [[ $bytes -gt $(( 1024**3 )) ]]; then |
local gibs=$(( $bytes / 1024**3 )) |
local mibs=$(( ( $bytes % 1024**3 ) / 1024**2 )) |
size="${gibs}.${mibs:0:2} GiB" |
elif [[ $bytes -gt $(( 1024**2)) ]]; then |
local mibs=$(( $bytes / 1024**2 )) |
local kibs=$(( ( $bytes % 1024**2 ) / 1024 )) |
size="${mibs}.${kibs:0:2} MiB" |
elif [[ $bytes -gt 1024 ]]; then |
local kibs=$(( $bytes / 1024 )) |
bytes=$(( $bytes % 1024 )) |
size="${kibs}.${bytes:0:2} KiB" |
else |
size="${bytes} B" |
fi |
echo $size |
} |
# Prints the size of a file in a human friendly form |
# The units are in the IEC/IEEE/binary format (e.g. MiB -for mebibytes- |
# instead of MB -for megabytes-) |
# get_pretty_file_size($1 = file) |
get_pretty_file_size() { |
local f="$1" |
local bytes=$(get_file_size "$f") |
get_pretty_size "$bytes" |
} |
# mv quiet |
# Move a file, be quiet about errors. |
# Ownership preservation is a common error on vfs, for example |
mvq() { |
mv -- "$@" 2>/dev/null |
} |
# Rename a file, if the target exists, try with appending numbers to the name |
# And print the output name to stdout |
# See $SAFE_RENAME_PATTERN |
# safe_rename($1 = original file, $2 = target file) |
# XXX: Note it fails if target has no extension |
safe_rename() { |
trace $@ |
local from="$1" |
local to="$2" |
# Output extension |
local ext=$(filext "$to") |
# Output filename without extension |
local b=${to%.$ext} |
local n=1 |
while [[ -f $to ]]; do # Only executes if $2 exists |
# Bash 2 and Bash 3 behave differently with substring replacement (${//}) and '%' |
# Sed is a safer bet |
to=$(sed -e "s#%b#$b#g" -e "s#%N#$n#g" -e "s#%e#$ext#g" <<<"$SAFE_RENAME_PATTERN") |
(( n++ )); |
done |
assert "[[ -n '${to//\'/\'\\\'\'}' ]]" # [[ -n '$to' ]] + escape single quotes |
mvq "$from" "$to" |
echo "$to" |
} |
# Gets the file size in bytes |
# get_file_size($1 = filename) |
# du can provide bytes or kilobytes depending on the version used. The difference |
# can be notorius... |
# Neither busybox's nor BSD's du allow --bytes. |
# Note that using "ls -H" is not an option for portability reasons either. |
get_file_size() { |
# First, try the extended du arguments: |
local bytes |
bytes=$(du -L --bytes "$1" 2>/dev/null) || { |
echo $(( 1024 * $(du -Lk "$1" | cut -f1) )) |
return |
} |
# Getting to here means the first du worked correctly |
cut -f1 <<<"$bytes" |
} |
# Du replacement. This differs from get_file_size in that it takes multiple arguments |
dur() { |
for file in $@ ; do |
get_file_size "$file" |
done |
} |
# Gets the size of the dvd device, in DVD mode |
get_dvd_size() { |
# FIXME: Case sensivity might break with iso9660 |
if [[ -f "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_1.VOB" ]]; then |
# Some VOBs available |
local vfiles="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_*.VOB" |
# Print all sizes, each on a line, add '+' to the end of each line, add 0 to the end. |
local feed="$(dur "$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_"*".VOB" | cut -f1 | sed 's/$/ + /') 0" |
get_pretty_size $(awkex "$(nltos "$feed")") |
else |
echo "?" |
fi |
} |
is_linux() { |
uname -s | grep -iq '^Linux$' |
} |
# Get the mountpoint of a mounted image. |
# This only works on Linux. *BSD normal users aren't able to use mdconfig -l |
# Is there any better way? |
# get_dvd_image_mountpoint($1 = image file) |
get_dvd_image_mountpoint() { |
if is_linux ; then |
local lodev=$(/sbin/losetup -j "$1" | cut -d':' -f1 | head -1) |
mount | grep "^$lodev " | cut -d' ' -f3 |
fi |
} |
# Tests the presence of all required programs |
# test_programs() |
test_programs() { |
local retval=0 last=0 |
local nopng=0 |
MPLAYER_BIN=$(type -pf mplayer) || true |
FFMPEG_BIN=$(type -pf ffmpeg) || true |
check_avail_tools |
# awk is required by SUS/POSIX but just to be sure... |
for prog in convert montage identify mktemp grep egrep cut sed awk ; do |
if ! type -pf "$prog" ; then |
error "Required program $prog not found!" |
(( retval++ ,1 )) |
fi >/dev/null |
done |
# TODO: [[x2]] |
# Early exit |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
# ImageMagick version. 6 is a must, I'm probably using some |
# features that require a higher minor version |
# Versions tested: |
# * Fedora 9: IM 6.4.0 |
local ver |
ver=$(convert -version | sed -n -e '1s/.*ImageMagick \([0-9][^ ]*\) .*$/\1/p;q') |
if [[ $ver ]]; then |
local verx=${ver//-/.}.0 # Extra .0 in case rev doesn't exist |
local major=$(cut -d'.' -f1 <<<"$verx") |
local minor=$(cut -d'.' -f2 <<<"$verx") |
local micro=$(cut -d'.' -f3 <<<"$verx") |
local rev=$(cut -d'.' -f4 <<<"$verx") |
local serial=$(( $major * 100000 + $minor * 10000 + $micro * 100 + $rev)) |
if [[ $serial -lt 630507 ]]; then |
error "ImageMagick 6.3.5-7 or higher is required. Found $ver." ; |
(( retval++ ,1 )) |
fi |
else |
error "Failed to check ImageMagick version." |
(( retval++ ,1 )) |
fi |
[[ $retval -eq 0 ]] || return $EX_UNAVAILABLE |
} |
# Test wether $GETOP is a compatible version; try to choose an alternate if |
# possible |
choose_getopt() { |
if ! type -pf "$GETOPT" ; then |
# getopt not in path |
error "Required program getopt not found!" |
return $EX_UNAVAILABLE |
fi >/dev/null |
local goe= gor=0 |
# Try getopt. If there's more than one in the path, try all of them |
for goe in $(type -paf $GETOPT) ; do |
"$goe" -T || gor=$? |
if [[ $gor -eq 4 ]]; then |
# Correct getopt found |
GETOPT="$goe" |
break; |
fi |
done >/dev/null |
if [[ $gor -ne 4 ]]; then |
error "No compatible version of getopt in path, can't continue." |
error " Enhanced getopt (i.e. GNU getopt) is required" |
return $EX_UNAVAILABLE |
fi |
return 0 |
} |
# Remove any temporary files |
# Does nothing if none has been created so far |
# cleanup() |
cleanup() { |
if [[ -z $TEMPSTUFF ]]; then return 0 ; fi |
inf "Cleaning up..." |
rm -rf "${TEMPSTUFF[@]}" |
unset VCSTEMPDIR |
unset TEMPSTUFF ; declare -a TEMPSTUFF |
} |
# Exit callback. This function is executed on exit (correct, failed or |
# interrupted) |
# exithdlr() |
exithdlr() { |
# I don't think that's really required anyway |
if [[ $RANDFUNCTION == 'filerand' ]]; then |
7<&- # Close FD 7 |
fi |
cleanup |
# XXX: In one of my computers a terminal reset is required |
#tset |
stty "$STTY" |
} |
# Feedback handling, these functions are use to print messages respecting |
# the verbosity level |
# Optional color usage added from explanation found in |
# <http://wooledge.org/mywiki/BashFaq> |
# |
# error($1 = text) |
error() { |
if [[ $VERBOSITY -ge $V_ERROR ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_ERR" |
# sgr0 is always used, this way if |
# a) something prints inbetween messages it isn't affected |
# b) if SIMPLE_FEEDBACK is overridden colour stops after the override |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
# It is important to redirect both tput and echo to stderr. Otherwise |
# n=$(something) wouldn't be colourised |
} |
# |
# Print a non-fatal error or warning |
# warning($1 = text) |
warn() { |
if [[ $VERBOSITY -ge $V_WARN ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_WARN" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print an informational message |
# inf($1 = text) |
inf() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_INF" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Print a debugging message |
# notice($1 = text) |
notice() { |
if [[ $VERBOSITY -gt $V_INFO ]]; then |
[[ $SIMPLE_FEEDBACK -eq 0 ]] && echo -n "$PREFIX_DBG" |
echo "$1$SUFFIX_FBACK" |
fi >&2 |
} |
# |
# Same as inf but with no colour ever. |
# infplain($1 = text) |
infplain() { |
if [[ $VERBOSITY -ge $V_INFO ]]; then |
echo "$1" >&2 |
fi |
} |
# |
# Buffering of feedback, usage: |
# buffered warn "my warning" |
# ... |
# flush_buffered |
# buffered($1 = feedback function, $2 = arguments) |
buffered() { |
local grab=$( $1 "$2" 2>&1 ) |
BUFFER=$BUFFER$grab$NL |
} |
# |
# Print buffered feedback to stderr |
# flush_buffered([$1 = indentation]) |
flush_buffered() { |
[[ ${BUFFER[*]} ]] || return 0 |
echo "$BUFFER" | sed -e '$d' -e "s/^/$1/g" >&2 # sed: delete last line, indent with $1 |
BUFFER='' |
} |
# |
# trace(... = function arguments) |
trace() { |
[[ $DEBUG -eq 1 ]] || return 0 |
[[ $INTERNAL_NO_TRACE -ne 1 ]] || return 0 |
local func=$(caller 0 | cut -d' ' -f2) # caller: <LINE>< ><FUNCTION>< ><FILE> |
if [[ -n $INTERNAL_TRACE_FILTER ]]; then |
if ! grep -Pq "$INTERNAL_TRACE_FILTER" <<<"$func" ; then |
return 0 |
fi |
fi |
notice "[TRACE]: $func ${*}" |
} |
# |
# Print the call stack / execution frames |
# callstack([$1 = first frame]=0) |
callstack() { |
[[ $DEBUG -eq 1 ]] || return 0 |
local frame=$1 c= fn= |
[[ -n $frame ]] || frame=0 |
echo "Callstack:" |
while : ; do |
c=$(caller $frame) || break |
c=${c% *} |
fn=${c#* } |
# Only the last one, main, won't be a function |
if [[ $(type -t $fn) == 'function' ]]; then |
fn="${fn}()" |
fi |
echo " ${fn}:${c% *}" |
(( ++frame )) |
done |
} |
# Print an error message and exit |
# die([$1 = message [, $2 = exit_code]]) |
# If no message is provided, use $ERROR_MSG |
# If no exit_code is provided, use $ERROR_CODE |
die() { |
local m=$1 ec=$2 |
[[ $ec ]] || ec=$ERROR_CODE |
[[ $ec ]] || ec=1 |
[[ $m ]] || m=$ERROR_MSG |
error "$m" |
exit $ec |
} |
# |
# Tests if the filter chain contains the provided filter |
# has_filter($1 = filtername) |
has_filter() { |
local filter= ref=$1 |
for filter in ${FILTERS_IND[@]} ; do |
[[ $filter == "$ref" ]] || continue |
return 0 |
done |
return 1 |
} |
# |
# Enables prefixes in console output (instead of colour) |
set_feedback_prefixes() { |
PREFIX_ERR='[E] ' |
PREFIX_INF='[i] ' |
PREFIX_WARN='[w] ' |
PREFIX_DBG='' |
SUFFIX_FBACK= |
} |
# |
# Initialises the variables affecting colourised feedback |
init_feedback() { |
HAS_COLORS= |
# tput might be preferable (Linux: man console_codes), but it doesn't |
# work on FreeBSD to set colors |
# Is tput available? |
if type -pf tput >/dev/null ; then |
# Is it able to set colours? |
if tput bold && [[ "-1" != "$(tput colors)" ]] && tput setaf 0 && tput sgr0; then |
PREFIX_ERR=$(tput bold; tput setaf 1) |
PREFIX_WARN=$(tput bold; tput setaf 3) |
PREFIX_INF=$(tput bold; tput setaf 2) |
PREFIX_DBG=$(tput bold; tput setaf 4) |
SUFFIX_FBACK=$(tput sgr0) |
HAS_COLORS="yes" |
else |
HAS_COLORS="no" |
set_feedback_prefixes |
fi >/dev/null |
fi |
if [[ -z $HAS_COLORS ]]; then |
# tput was not an option, let's try ANSI escape codes instead [[AEC]] |
# TODO: Detect support |
# Alternatively: $ perl -e 'print "\e[31m\e[1m"' |
# echo -e is not portable but echo $'' is bash-specific so it should be fine... |
# except when ANSI escape codes aren't supported of course |
PREFIX_ERR=$(echo $'\033[1m\033[31m') |
PREFIX_WARN=$(echo $'\033[1m\033[33m') |
PREFIX_INF=$(echo $'\033[1m\033[32m') |
PREFIX_DBG=$(echo $'\033[1m\033[34m') |
SUFFIX_FBACK=$(echo $'\033[0m') |
HAS_COLORS="yes" |
fi |
# Finally, if there's no colour support, use prefixes instead |
if [[ -z $HAS_COLORS ]]; then |
set_feedback_prefixes |
fi |
} |
# |
# seq replacement |
# seq is not always present, jot is an alternative on FreeBSD. Instead, this is |
# a direct replacement |
# Note pure bash is *slower* than the awk (or perl) version |
# seqr($1 = from, $2 = to, $3 = increment) |
seqr() { |
local from=$1 to=$2 inc=$3 |
[[ $inc ]] || inc=1 |
awk "BEGIN { for (i=$from;i<=$to;i+=$inc) print i }" |
} |
# assertion operator |
# Note: Use single quotes for globals, no need to expand in release |
# assert(... = code) |
assert() { |
[[ $RELEASE -eq 0 ]] || { |
function assert { :; } # Redefine to avoid check |
} |
local c=$(caller 0) # <num> <func> <file> |
c=${c% *} # <num> <func> |
local LIN=${c% *} FN=${c#* } |
eval "$@" || { |
error "Internal error at $FN():$LIN: $@" |
local cal=$(caller 1) |
[[ $level ]] && error " Stack trace:" |
local level=2 |
error "$(callstack 1 | sed 's/^/ /')" |
exit $EX_SOFTWARE |
} |
} |
# Conditional assertion |
# assert_if($1 = condition, $2 = assert if $1 true) |
assert_if() { |
[[ $RELEASE -eq 1 ]] && return |
if eval "$1" ; then |
assert "$2" |
fi |
} |
# }}} # Convenience functions |
# {{{ # Core functionality |
# {{{{ # Mplayer support |
# Check for mplayer |
mplayer_test_avail() { |
MPLAYER_BIN=$(type -pf mplayer 2>/dev/null) |
[[ $MPLAYER_BIN ]] && { |
if ! "$MPLAYER_BIN" -vo help 2>&1 | grep -q 'png' ; then |
warn "MPlayer can't output to png, won't be able to use it." |
unset MPLAYER_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using mplayer |
# Fills $MPLAYER_CACHE with the relevant output and $MPLAYER_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
mplayer_identify() { |
trace $@ |
assert '[[ $MPLAYER_BIN ]]' |
local f="$1" |
local mi=( ) |
# Note to self: Don't change the -vc as it would affect $vdec |
if [[ $DVD_MODE -eq 0 ]]; then |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet "$f" 2>"$STDERR" | grep '^ID') |
else |
MPLAYER_CACHE=$("$MPLAYER_BIN" -benchmark -ao null -vo null -identify -frames 0 \ |
-quiet -dvd-device "$f" dvd://$DVD_TITLE \ |
2>"$STDERR" | grep '^ID') |
fi |
# Note the head -1! |
mi[$VCODEC]=$(grep ID_VIDEO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # FourCC |
mi[$ACODEC]=$(grep ID_AUDIO_FORMAT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$VDEC]=$(grep ID_VIDEO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) # Decoder (!= Codec) |
mi[$W]=$(grep ID_VIDEO_WIDTH <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$H]=$(grep ID_VIDEO_HEIGHT <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
mi[$FPS]=$(grep ID_VIDEO_FPS <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
# For some reason my (one track) samples have two ..._NCH, first one 0 |
#+Also multichannel is detected as 2 ch |
mi[$CHANS]=$(grep ID_AUDIO_NCH <<<"$MPLAYER_CACHE"| grep -v '=0' | cut -d'=' -f2|head -1) |
if [[ $DVD_MODE -eq 0 ]]; then |
# For DVDs it prints ID_DVD_TITLE_x_LENGTH and ID_LENGTH. |
#+Both appear valid. |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| cut -d'=' -f2) |
[[ ${mi[$LEN]} ]] || mi[$LEN]=$(grep ID_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
else |
mi[$LEN]=$(grep ID_DVD_TITLE_${DVD_TITLE}_LENGTH <<<"$MPLAYER_CACHE"| head -1 | cut -d'=' -f2) |
fi |
# Voodoo :P Remove (one) trailing zero |
if [[ "${mi[$FPS]:$(( ${#mi[$FPS]} - 1 ))}" == '0' ]]; then |
mi[$FPS]="${mi[$FPS]:0:$(( ${#mi[$FPS]} - 1 ))}" |
fi |
mi[$ASPECT]=$(grep ID_VIDEO_ASPECT <<<"$MPLAYER_CACHE" | egrep -v '^0.0000$' | cut -d'=' -f2 | tail -1) |
# If none set, delete it |
[[ ${mi[$ASPECT]} ]] && fptest "${mi[$ASPECT]}" -eq 0.0 && mi[$ASPECT]='' |
mi[$VCNAME]=$(get_vcodec_name "${mi[$VCODEC]}") |
if [[ ( ${mi[$VDEC]} == 'ffodivx' ) && ( ${mi[$VCNAME]} != 'MPEG-4' ) ]]; then |
mi[$VCNAME]="${mi[$VCNAME]} (MPEG-4)" |
elif [[ ${mi[$VDEC]} == 'ffh264' ]]; then # At least two different fourccs use h264, maybe more |
mi[$VCNAME]="${mi[$VCNAME]} (h.264)" |
fi |
mi[$ACNAME]=$(get_acodec_name "${mi[$ACODEC]}") |
if [[ ${mi[$ACODEC]} == 'samr' ]] ; then |
local adec=$(grep ID_AUDIO_CODEC <<<"$MPLAYER_CACHE" | head -1 | cut -d'=' -f2) |
if [[ $adec == 'ffamrnb' ]]; then |
mi[$ACNAME]="AMR-NB"; |
fi |
fi |
# Warn if a known pitfall is found |
# See above for 1000 fps |
[[ ${mi[$FPS]} == '1000.00' ]] && \ |
warn "Possible inaccuracy in FPS detection." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Number of channels 0 happened for WMA in non-x86 |
[[ ${mi[$CHANS]} == '0' ]] && \ |
warn "Failed to detect number of audio channels." && \ |
warn " Install both mplayer and ffmpeg for better detection." |
# Array assignment |
MPLAYER_ID=("${mi[@]}") |
RESULT=("${mi[@]}") |
} |
# Capture a frame with mplayer |
# mplayer_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra options]) |
mplayer_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local ts=$2 |
local cap=00000005.png o=$3 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
# Capture 5 frames and drop the first 4, fixes a weird bug/feature of mplayer ([M1]) |
assert '[[ $DVD_MODE -ne 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" $4 "$f" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
# Capture a frame with mplayer |
# mplayer_dvd_capture($1 = inputfile, $2 = timestamp, $3 = output) |
mplayer_dvd_capture() { |
trace $@ |
# Note mplayer CAN'T set the output filename, newer mplayer can set output |
#+dir though. |
local f="$1" |
local cap=00000005.png o=$3 |
local ts=$2 |
# No point in passing ms to mplayer |
ts=$(cut -d'.' -f1 <<<"$ts") |
assert '[[ $DVD_MODE -eq 1 ]]' |
"$MPLAYER_BIN" -sws 9 -ao null -benchmark -vo "png:z=0" -quiet \ |
-frames 5 -ss "$ts" -dvd-device "$f" \ |
$4 "dvd://$DVD_TITLE" >"$STDOUT" 2>"$STDERR" |
rm -f 0000000{1,2,3,4}.png # Remove the first four |
[[ ( -f $cap ) && ( '0' != "$(du "$cap" | cut -f1)" ) ]] && { |
[[ $cap == "$o" ]] || mvq "$cap" "$o" |
} |
} |
mplayer_probe() { |
local r= f=00000005.png |
if [[ $DVD_MODE -eq 1 ]]; then |
mplayer_dvd_capture "$1" "$2" "$f" "-vf scale=96:96" |
else |
mplayer_capture "$1" "$2" "$f" "-vf scale=96:96" |
fi |
r=$? |
rm -f "$f" # Must be manually removed since this runs before process() |
return $r |
} |
# }}}} # Mplayer support |
# {{{{ # FFmpeg support |
# Check for ffmpeg |
ffmpeg_test_avail() { |
FFMPEG_BIN=$(type -pf ffmpeg 2>/dev/null) |
# Test we can actually use FFmpeg |
[[ $FFMPEG_BIN ]] && { |
# Newer FF has -codecs, -formats, -protocols, older has only -formats |
#+png is a codec so it's on different lists on newer and older |
if ! "$FFMPEG_BIN" -formats 2>/dev/null | grep -q 'EV.* png' && \ |
! "$FFMPEG_BIN" -codecs 2>/dev/null | grep -q 'EV.* png' ; then |
warn "FFmpeg can't output to png, won't be able to use it." |
unset FFMPEG_BIN |
return $EX_UNAVAILABLE |
fi |
} |
} |
# Try to identify video properties using ffmpeg |
# Fills $FFMPEG_CACHE with the relevant output and $FFMPEG_ID with |
# the actual values. See identify_video() |
# mplayer_identify($1 = file) |
ffmpeg_identify() { |
trace $@ |
assert '[[ $FFMPEG_BIN ]]' |
local f="$1" |
# DVD Devices *MUST* be mounted for the identifying process to even start |
assert '[[ $DVD_MODE -eq 0 || $DVD_MOUNTP ]]' |
if [[ $DVD_MODE -eq 1 ]]; then |
local vfile="$DVD_MOUNTP/VIDEO_TS/VTS_${DVD_VTS}_0.VOB" |
if [[ ! -r $vfile ]]; then |
error "Failed to locate mounted DVD. Detection will be less accurate." |
return 0 # We can continue anyway |
fi |
f="$vfile" |
fi |
# XXX: FFmpeg detects mpeg1video in DVDs?? |
local fi=( ) vs= as= obs= vsid= |
# FFmpeg is relatively new, introduced in 1.0.99 so it needs more testing |
FFMPEG_CACHE=$("$FFMPEG_BIN" -i "$f" -dframes 0 -vframes 0 /dev/null 2>&1 | egrep '(Stream|Duration:|^Seems)') |
# Only the first streams of each type are honored. FIXME: Add multi-audio support. |
vs=$(sed -n -e '/Stream/!d' -e '/Video:/!d' -e '/Video:/p;q' <<<"$FFMPEG_CACHE") |
as=$(sed -n -e '/Stream/!d' -e '/Audio:/!d' -e '/Audio:/p;q' <<<"$FFMPEG_CACHE") |
obs=$(grep Seems <<<"$FFMPEG_CACHE") |
# Stream #0.0: Video: mpeg4, yuv420p, 624x352 [PAR 1:1 DAR 39:22], 23.98 tbr, 23.98 tbn, 24k tbc |
# New and old versions of ffmpeg changed their output considerably, e.g.: |
# (same file, Robotica_720.wmv) |
# New output: |
# Seems stream 1 codec frame rate differs from container frame rate: 1000.00 (1000/1) -> 23.98 (24000/1001) |
# [...] |
# Duration: 00:00:20.77, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0(eng): Audio: wmapro, 48000 Hz, 6 channels, s16, 384 kb/s |
# Stream #0.1(eng): Video: wmv3, yuv420p, 1280x720, 6500 kb/s, 23.98 tbr, 1k tbn, 1k tbc |
# Old output: |
# Duration: 00:00:20.7, start: 3.000000, bitrate: 6250 kb/s |
# Stream #0.0: Audio: 0x0162, 48000 Hz, 5:1, 384 kb/s |
# Stream #0.1: Video: wmv3, yuv420p, 1280x720, 24.00 fps(r) |
# TODO: tbr is rounded to two decimals but the actual ratio is printed: |
# 24000/1001 = 23.97602 |
# (older ffmpeg prints 24 fps, 24/1 so no luck here |
# **Also seen**: (note the 'tb(r)') |
# Stream #0.1: Video: wmv3, yuv420p, 1440x1080 [PAR 4:3 DAR 16:9], 8000 kb/s, 23.98 tb(r) |
# **Also seen**: (VOB, latest ffmpeg as of this writing): |
# Stream #0.0[0x1e0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 9800 kb/s, 23.53 fps, 25 tbr, 90k tbn, 50 tbc |
# **Also seen**: (DVB TS to DX50 in MKV), note the DAR mess, the second one is the correct one |
# Stream #0.0: Video: mpeg4, yuv420p, 640x326 [PAR 1:1 DAR 320:163], PAR 231:193 DAR 73920:31459, 25 fps, 25 tbr, 1k tbn, 25 tbc |
vsid=$(sed -n -e 's/^.*#0\.\([0-9]\).*$/\1/p' <<<"$vs") # Video Stream ID |
fi[$VCODEC]=$(sed -n -e 's/^.*Video: \([^,]*\).*$/\1/p' <<<"$vs") |
# ffmpeg's codec might contain spaces in some cases, i.e. iv4 in mov (see mplayer's bestiary) |
#+unless this turns out to be common I won't be handling it specially |
# Note unidentified audio codecs will be printed in hexadecimal |
fi[$ACODEC]=$(sed -n -e 's/^.*Audio: \([^,]*\).*$/\1/p' <<<"$as") |
fi[$VDEC]='' |
# The comma is required for cases where the stream id is printed (in hex) |
fi[$W]=$(sed -n -e 's/^.*, \([0-9]*\)x[0-9].*$/\1/p' <<<"$vs") |
fi[$H]=$(sed -n -e 's/^.*, [0-9]*x\([0-9]*\).*$/\1/p' <<<"$vs") |
# Newer CHANS and some older... |
fi[$CHANS]=$(sed -n -e 's/.*\([0-9][0-9]*\) channels.*/\1/p' <<<"$as") |
# ...fallback for older |
if [[ -z ${fi[$CHANS]} ]]; then |
local chans=$(sed -n -e 's/.*Hz, \([^, ][^, ]*\).*$/\1/p' <<<"$as") |
case $chans in |
mono) fi[$CHANS]=1 ;; |
stereo) fi[$CHANS]=2 ;; |
5.1|5:1) fi[$CHANS]=6 ;; # * |
*) ;; # Other layouts use 'N channels' |
# 5.1 was in the previous version (can't remember if it was empirical). |
esac |
fi |
# Newer FPS... |
# tbr/tbn/tbc explanation: tb stands for time base |
# n: AVStream, c: AVCodecContext, r: VideoStream (Guessed) |
# tbr is the best bet. Note it's common for WMVs to contains "1k tbn, 1k tbc" |
# tbr is rounded to two decimals, the values used to derived it might be |
# printed in a "Seems ..." line like the one in the example above so it |
# can be re-calculated. |
fi[$FPS]=$(egrep -o '[0-9]*\.?[0-9]*k? tb(r|\(r\))' <<<"$vs" | cut -d' ' -f1) |
# Let's convert e.g. 23.98 into 23.976...: |
if [[ ${fi[$FPS]} ]] && grep -q '\.' <<<"${fi[$FPS]}" ; then |
# Decimals, see if we got better values available |
local vsobs=$(grep "stream $vsid" <<<"$obs") |
# Observations regarding video stream found |
if [[ $vsobs ]] && grep -q " -> ${fi[$FPS]} (.*)" <<<"$vsobs" ; then |
# FPS candidate |
local newfps=$(egrep -o -- '-> [^ ]* \([0-9]*/[0-9]*' <<<"$vsobs" | cut -d'(' -f2) |
is_fraction $newfps && fi[$FPS]=$(keepdecimals "$newfps" 3) |
fi |
fi |
# ...fallback for older. The older version I tried seems to round further, i.e. |
# 23.976 became 24 so no fix for this one |
if [[ -z ${fi[$FPS]} ]]; then |
# No k suffix here, 1000 is 1000 |
fi[$FPS]=$(sed 's/.*, \([0-9]*\.[0-9]*\) fps.*/\1/' <<<"$vs") |
fi |
# Be consistent with mplayer's output: at least two decimals |
[[ ${fi[$FPS]} ]] && { |
fi[$FPS]=$(keepdecimals "${fi[$FPS]}" 3) |
fi[$FPS]=${fi[$FPS]/%0} # Strip 0$ |
} |
fi[$LEN]=$(sed -n -e '/Duration: /!d' \ |
-e 's/.*Duration: \([^,][^,]*\).*/\1/p;q' <<<"$FFMPEG_CACHE") |
if [[ ${fi[$LEN]} == 'N/A' ]]; then # It might be unable to detect |
fi[$LEN]="" |
fi |
fi[$LEN]=$( get_interval $(echo "${fi[$LEN]}" | sed -e 's/:/h/' -e 's/:/m/') ) |
# Aspect ratio in FFmpeg is only provided in newer ffmpeg |
# It might be calculated for files without one (which is ok anyway) |
# Must only match the last DAR (see the double DAR example above) |
fi[$ASPECT]=$(sed -n -e '/DAR [0-9]/!d' \ |
-e 's#.*DAR \([0-9]*\):\([0-9]*\).*#\1/\2#p;q' <<<"$FFMPEG_CACHE") |
# Due to calling ffmpeg on a single VOB when in DVD Device mode, the length will be partial |
[[ $DVD_MODE -eq 0 ]] || fi[$LEN]='' |
fi[$VCNAME]=$(get_vcodec_name $(translate_ffmpeg_vcodec_id "${fi[$VCODEC]}")) |
fi[$ACNAME]=$(get_acodec_name $(translate_ffmpeg_acodec_id "${fi[$ACODEC]}")) |
if [[ "${fi[$VCODEC]}" == 'h264' ]]; then |
fi[$VCNAME]="${fi[$VCNAME]} (h.264)" |
fi |
FFMPEG_ID=("${fi[@]}") |
RESULT=("${fi[@]}") |
} |
ffmpeg_probe() { |
local tfile=$(new_temp_file '-probe.png') |
ffmpeg_capture "$1" "$2" "$tfile" "-s 96x96" |
} |
# Capture a frame with ffmpeg |
# ffmpeg_capture($1 = inputfile, $2 = timestamp, $3 = output[, $4 = extra opts]) |
ffmpeg_capture() { |
trace $@ |
local f=$1 |
local ts=$2 |
local o=$3 |
# XXX: It would be nice to show a message if it takes too long |
# See wa_ss_* declarations at the start of the file for details |
"$FFMPEG_BIN" -y ${wa_ss_be/ / $ts} -i "$f" ${wa_ss_af/ / $ts} -an \ |
-dframes 1 -vframes 1 -vcodec png \ |
-f rawvideo $4 "$o" >"$STDOUT" 2>"$STDERR" |
[[ ( -f $o ) && ( '0' != "$(du "$o" | cut -f1)" ) ]] |
} |
# }}}} # FFmpeg support |
# {{{{ # Classic identification (combined mplayer & ffmpeg) |
# Test availability |
classic_test_avail() { |
mplayer_test_avail && ffmpeg_test_avail |
} |
# }}}} # Classic identification |
# Sets the tool to use as a capturer |
# Possible tool names: ffmpeg, mplayer |
# set_capturer($1 = tool, [$2 = user picked]=1) |
set_capturer() { |
trace $@ |
local up=$2 |
[[ -n $up ]] || up=1 |
if [[ $up -eq 1 ]] && ! grep -q "$1" <<<"${CAPTURERS_AVAIL[*]}" ; then |
error "Tried to set '$1' as capturer, but not available" |
return 1 |
fi |
if [[ $1 = mplayer ]]; then |
DECODER=$DEC_MPLAYER |
CAPTURER=mplayer |
CAPTURER_HAS_MS=0 |
elif [[ $1 = ffmpeg ]]; then |
DECODER=$DEC_FFMPEG |
CAPTURER=ffmpeg |
CAPTURER_HAS_MS=1 |
else |
assert false |
fi |
if [[ $up -eq 1 ]]; then |
USR_DECODER=$DECODER |
USR_CAPTURER=$CAPTURER |
fi |
} |
# Creates a new temporary directory |
# create_temp_dir() |
create_temp_dir() { |
trace $@ |
[[ -z $VCSTEMPDIR ]] || return 0 |
# Try to use /dev/shm if available, this provided a very small |
# benefit on my system but me of help for huge files. Or maybe won't. |
# Passing a full path template is more x-platform than using |
# -t / -p |
if [[ ( -d /dev/shm ) && ( -w /dev/shm ) ]]; then |
VCSTEMPDIR=$(mktemp -d /dev/shm/vcs.XXXXXX) |
else |
[[ $TMPDIR ]] || TMPDIR="/tmp" |
VCSTEMPDIR=$(env TMPDIR="$TMPDIR" mktemp -d "$TMPDIR/vcs.XXXXXX") |
fi |
if [[ ! -d $VCSTEMPDIR ]]; then |
error "Error creating temporary directory" |
return $EX_CANTCREAT |
fi |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$VCSTEMPDIR" ) |
} |
# Resolve path. Realpath is not always available and readlink [[LC]] behaves differently in |
# GNU and BSD. |
# XXX: Has AWK or bash something similar? This is the only place requiring perl! |
# realpathr($1 = path) -> canonical path |
realpathr() { |
perl -e "use Cwd qw(realpath);print realpath('$1')" |
} |
# Create a new temporal file and print its filename |
# new_temp_file($1 = suffix) |
new_temp_file() { |
trace $@ |
local r=$(env TMPDIR="$VCSTEMPDIR" mktemp "$VCSTEMPDIR/vcs-XXXXXX") |
if [[ ! -f $r ]]; then |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
fi |
r=$(safe_rename "$r" "$r$1") || { |
error "Failed to create temporary file" |
return $EX_CANTCREAT |
} |
TEMPSTUFF=( "${TEMPSTUFF[@]}" "$r" ) |
echo "$r" |
} |
# Randomises the colours and fonts. The result won't be of much use |
# in most cases but it might be a good way to discover some colour/font |
# or colour combination you like. |
# randomize_look() |
randomize_look() { |
trace $@ |
local mode=f lineno |
if [[ $mode == 'f' ]]; then # Random mode |
# There're 5 rows of extra info printed |
local ncolours=$(( $(convert -list color | wc -l) - 5 )) |
randcolour() { |
lineno=$(( 5 + ( $(rand) % $ncolours ) )) |
convert -list color | sed -n "${lineno}{p;q;}" | cut -d' ' -f1 # [[R1#11]] |
} |
else # Pseudo-random mode, WIP! |
randccomp() { |
# colours are in the 0..65535 range, while RANDOM in 0..32767 |
echo $(( $(rand) + $(rand) + ($(rand) % 1) )) |
} |
randcolour() { |
echo "rgb($(randccomp),$(randccomp),$(randccomp))" |
} |
fi |
# Older IM output was pretty different. Since this is a mode used for testing |
# I don't believe it's worth the effort to get it always right |
# This used to be -list type. Was this an older IM version or a bug in vcs? |
local nfonts=$(convert -list font | grep '^\s*Font:' | wc -l) |
randfont() { |
lineno=$(( $(rand) % $nfonts )) |
convert -list font | sed -n -e '/Font: ./!d' -e 's/^.*Font: //' -e "${lineno}{p;q}" |
} |
BG_HEADING=$(randcolour) |
BG_SIGN=$(randcolour) |
BG_TITLE=$(randcolour) |
BG_CONTACT=$(randcolour) |
FG_HEADING=$(randcolour) |
FG_SIGN=$(randcolour) |
FG_TSTAMPS=$(randcolour) |
FG_TITLE=$(randcolour) |
FONT_TSTAMPS=$(randfont) |
FONT_HEADING=$(randfont) |
FONT_SIGN=$(randfont) |
FONT_TITLE=$(randfont) |
inf "Randomisation result: |
Chosen backgrounds: |
'$BG_HEADING' for the heading |
'$BG_SIGN' for the signature |
'$BG_TITLE' for the title |
'$BG_CONTACT' for the contact sheet |
Chosen font colours: |
'$FG_HEADING' for the heading |
'$FG_SIGN' for the signature |
'$FG_TITLE' for the title |
'$FG_TSTAMPS' for the timestamps, |
Chosen fonts: |
'$FONT_HEADING' for the heading |
'$FONT_SIGN' for the signature |
'$FONT_TITLE' for the title |
'$FONT_TSTAMPS' for the timestamps" |
unset -f randcolour randfound randccomp |
} |
# Add to $TIMECODES the timecodes at which a capture should be taken |
# from the current video |
# compute_timecodes($1 = timecode_from, $2 = interval, $3 = numcaps) |
compute_timecodes() { |
trace $@ |
local st=0 end=${VID[$LEN]} tcfrom=$1 tcint=$2 tcnumcaps=$3 eo=0 |
local eff_eo= # Effective end_offset (for percentages) |
# globals: $FROMTIME, $TOTIME, $TIMECODE_FROM, $TIMECODES, $END_OFFSET |
if fptest $st -lt $FROMTIME ; then |
st=$FROMTIME |
fi |
if fptest $TOTIME -gt 0 && fptest $end -gt $TOTIME ; then |
end=$TOTIME |
fi |
if is_percentage $END_OFFSET ; then |
eff_eo=$(percent $end $END_OFFSET) |
else |
eff_eo=$(get_interval "$END_OFFSET") |
fi |
if fptest $TOTIME -le 0 ; then # If no totime is set, use END_OFFSET |
eo=$eff_eo |
local runlen=$(awkexf "$end - $st") |
if fptest "($end-$eo-$st)" -le 0 ; then |
if fptest "$eo" -gt 0 && [[ -z $USR_END_OFFSET ]] ; then |
warn "Default end offset was too high for the video, ignoring it." |
eo=0 |
else |
error "End offset too high, use e.g. '-E0'." |
return $EX_UNAVAILABLE |
fi |
fi |
fi |
local inc= |
if [[ $tcfrom -eq $TC_INTERVAL ]]; then |
inc=$tcint |
elif [[ $tcfrom -eq $TC_NUMCAPS ]]; then |
# Numcaps mandates: timecodes are obtained dividing the length |
# by the number of captures |
if [[ $tcnumcaps -eq 1 ]]; then # Special case, just one capture, center it |
inc=$(awkexf "(($end-$st)/2 + 1)") |
else |
inc=$(awkexf "(($end-$eo-$st)/$tcnumcaps)") |
fi |
else |
error "Internal error" |
return $EX_SOFTWARE |
fi |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
inc=$(keepdecimals_lower $inc 0) |
else |
# Keep three decimals, round to lower to avoid exceeding the video length |
inc=$(keepdecimals_lower $inc 3) |
fi |
if fptest $inc -gt ${VID[$LEN]}; then |
error "Capture interval is longer than video length, skipping '$f'" |
return $EX_USAGE |
fi |
if fptest $inc -eq 0; then |
error "Capture interval is too low, skipping '$f'" |
return $EX_UNAVAILABLE |
fi |
local stamp=$st |
local -a LTC |
local bound=$(awkexf "$end - $eo") |
local last= |
while fptest $stamp -le "$bound"; do |
# Due to rounding (i.e. with mplayer), the loop might need an extra run |
# to reach the end of the video. |
# Ensure it doesn't if the user requested a specific number of captures |
if [[ ( $tcfrom -eq $TC_NUMCAPS ) && ( ${#LTC[@]} -gt $tcnumcaps ) ]]; then |
break |
fi |
assert fptest $stamp -ge 0 |
LTC=( "${LTC[@]}" "$stamp" ) |
last=$stamp |
stamp=$(keepdecimals_lower $(awkexf "$stamp + $inc") 3) |
done |
local lower_bound=$(awkexf "$st + $inc") |
inf "Capturing in range [$(pretty_stamp $lower_bound)-$(pretty_stamp $last)]. Total length: $(pretty_stamp ${VID[$LEN]})" |
unset LTC[0] # Discard initial cap (=$st) |
TIMECODES=( "${TIMECODES[@]}" "${LTC[@]}" ) |
} |
# Tries to guess an aspect ratio comparing width and height to some |
# known values (e.g. VCD resolution turns into 4/3) |
# guess_aspect($1 = width, $2 = height) |
guess_aspect() { |
trace $@ |
local w=$1 h=$2 ar |
case "$w" in |
352) |
if [[ ( $h -eq 288 ) || ( $h -eq 240 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# VCD / DVD @ VCD Res. / Half-D1 / CVD |
ar=4/3 |
elif [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then |
# Ambiguous, could perfectly be 16/9 |
# Half-D1 / CVD |
ar=4/3 |
fi |
;; |
704|720) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # DVD / DVB |
# Ambiguous, could perfectly be 16/9 |
ar=4/3 |
fi |
;; |
480) |
if [[ ( $h -eq 576 ) || ( $h -eq 480 ) ]]; then # SVCD |
ar=4/3 |
fi |
;; |
esac |
if [[ -z $ar ]]; then |
if [[ ( $h -eq 720 ) || ( $h -eq 1080 ) ]]; then # HD |
ar=16/9 |
fi |
fi |
if [[ -z $ar ]]; then |
warn "Couldn't guess aspect ratio." |
ar="$w/$h" # Don't calculate it yet |
fi |
echo $ar |
} |
# FIXME: Re-order captures when moved |
# Capture a frame |
# Sets $RESULT to the timestamp actually used |
# capture($1 = filename, $2 = output file, $3 = second, [$4 = disable blank frame evasion]) |
capture() { |
trace $@ |
local f=$1 out=$2 stamp=$3 prevent_evasion=$4 |
local alternatives= alt= delta= |
if [[ $prevent_evasion != '1' ]]; then |
for delta in $EVASION_ALTERNATIVES ; do |
alt=$(awkexf "$stamp + $delta") |
if fptest $alt -gt 0 && fptest $alt -lt "${VID[$LEN]}" ; then |
alternatives+=( $alt ) |
fi |
done |
fi |
capture_and_evade "$1" "$2" "$3" ${alternatives[*]} |
# Correct the timestamp in case it had to be adjusted |
local nstamp=$(echo "$CAPTURES" | tail -2 | head -1 | cut -d':' -f1) |
if fptest "int($stamp)" -ne "int($nstamp)" ; then |
inf " Capture point changed to $( pretty_stamp $nstamp )" |
stamp=$nstamp |
fi |
RESULT=$stamp |
} |
# Capture a frame, retry a few times if a blank frame is detected. Use capture() |
# Appends '$timestamp:$output\n' to $CAPTURES |
# capture_and_evade($1 = filename, $2 = output file, $3 = second, $4... = alternate seconds) |
capture_and_evade() { |
trace $@ |
local f=$1 stamp=$3 ofile=$2 |
shift 2 |
local tscand= |
while [[ -n $1 ]]; do |
tscand=$1 |
shift |
if ! capture_impl "$f" "$tscand" "$ofile" ; then |
error "Failed to capture frame at $(pretty_stamp $stamp) (${stamp}s)" |
return $EX_SOFTWARE |
fi |
# **XXX: EXPERIMENTAL: Blank frame evasion, initial test implementation |
local blank_val=$(convert "$ofile" -colorspace Gray -format '%[fx:image.mean*100]' info:) |
local upper=$(( 100 - $BLANK_THRESHOLD )) |
if fptest $blank_val -lt $BLANK_THRESHOLD || fptest $blank_val -gt $upper ; then |
local msg=" Blank (enough) frame detected." |
if [[ -n $1 ]]; then |
msg+=" Retrying at $(pretty_stamp $1)." |
else |
msg+=" Giving up." |
fi |
warn "$msg" |
else |
# No need to evade |
break |
fi |
# /XXX |
done |
CAPTURES="$CAPTURES$RESULT$NL" |
} |
# Capture a frame, intermediate-level implementation, use capture() instead. |
# Sets $RESULT to '$timestamp:$output' |
# Sets $CAPTURED_FROM_CACHE to 1 if it was already captured |
# capture_impl($1 = filename, $2 = second, $3 = output file) |
capture_impl() { |
trace $@ |
local f=$1 stamp=$2 ofile=$3 |
RESULT='' |
CAPTURED_FROM_CACHE=0 |
# Avoid recapturing if timestamp is already captured. |
# The extended set includes the standard set so when using the extended mode |
#+this will avoid some captures, specially with mplayer, since it doesn't |
#+have ms precission |
# FIXME: This often won't work with ffmpeg since there might be a slight |
# difference in ms. |
local key= |
# Normalise key values' decimals |
if [[ $CAPTURER_HAS_MS -eq 0 ]]; then |
key=$(awkex "int($stamp)") |
else |
key=$(awkex $stamp) |
fi |
local cached=$(grep "^$key:" <<<"$CAPTURES" | head -1) |
if [[ $cached ]]; then |
notice "Skipped capture at $(pretty_stamp $key)" |
cp "${cached#*:}" "$ofile" # TODO: Is 'cp -s' safe? |
CAPTURED_FROM_CACHE=1 |
else |
local capfn=${CAPTURER}_capture |
if [[ $DVD_MODE -eq 1 ]]; then |
capfn=${CAPTURER}_dvd_capture |
fi |
$capfn "$f" "$stamp" "$ofile" || { |
return $EX_SOFTWARE |
} |
fi |
RESULT="$key:$ofile" |
} |
# Applies all individual vidcap filters |
# filter_vidcap($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index[1..]) |
filter_vidcap() { |
trace $@ |
# For performance purposes each filter adds a set of options |
# to 'convert'. That's less flexible but right enough now for the current |
# filters. |
local f=$1 t=$2 w=$3 h=$4 c=$5 i=$6 |
local cmdopts= |
for filter in ${FILTERS_IND[@]}; do |
$filter "$f" "$t" "$w" "$h" "$c" "$i" # Sets $RESULT |
cmdopts="$cmdopts $RESULT -flatten " |
done |
local t=$(new_temp_file .png) |
eval "convert -background transparent -fill transparent '$1' $cmdopts '$t'" |
# If $t doesn't exist returns non-zero |
[[ -f $t ]] && mvq "$t" "$1" |
} |
# Applies all global vidcap filters |
#filter_all_vidcaps() { |
# # TODO: Do something with "$@" |
# true |
#} |
filt_resize() { |
trace $@ |
local f="$1" t=$2 w=$3 h=$4 |
# Note the '!', required to change the aspect ratio |
RESULT=" \( -geometry ${w}x${h}! \) " |
} |
# Draw a timestamp in the file |
# filt_apply_stamp($1 = filename, $2 = timestamp, $3 = width, $4 = height, $5 = context, $6 = index) |
filt_apply_stamp() { |
trace $@ |
local filename=$1 timestamp=$2 width=$3 height=$4 context=$5 index=$6 |
local pts=$PTS_TSTAMPS |
if [[ $height -lt 200 ]]; then |
pts=$(( $PTS_TSTAMPS / 3 )) |
elif [[ $height -lt 400 ]]; then |
pts=$(( $PTS_TSTAMPS * 2 / 3 )) |
fi |
# If the size is too small they won't be readable at all |
# With the original font 8 was the minimum, with DejaVu 7 is readable |
if [[ $pts -le 7 ]]; then |
pts=7 |
if [[ ( $index -eq 1 ) && ( $context -ne $CTX_EXT ) ]]; then |
warn "Very small timestamps in use. Disabling them with -dt might be preferable" |
fi |
fi |
# The last -gravity None is used to "forget" the previous gravity (otherwise it would |
# affect stuff like the polaroid frames) |
RESULT=" \( -box '$BG_TSTAMPS' -fill '$FG_TSTAMPS' -stroke none -pointsize '$pts' " |
RESULT+=" -gravity '$GRAV_TIMESTAMP' -font '$FONT_TSTAMPS' -strokewidth 3 -annotate +5+5 " |
RESULT+=" ' $timestamp ' \) -flatten -gravity None " |
} |
# Apply a framed photo-like effect |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_photoframe($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_photoframe() { |
trace $@ |
# Tweaking the size gives a nice effect too |
# w=$(( $w - ( $RANDOM % ( $w / 3 ) ) )) |
# The border is relative to the input size (since 1.0.99), with a maximum of 6 |
# Should probably be bigger for really big frames |
# Note that only images below 21600px (e.g. 160x120) go below a 6px border |
local border=$(( ($3*$4) / 3600 )) |
[[ $border -lt 7 ]] || border=6 |
RESULT="-bordercolor white -border $border -bordercolor grey60 -border 1 " |
} |
filt_softshadow() { |
# Before this was a filter, there was the global (montage) softshadow (50x2+10+10) and the |
# photoframe inline softshadow 60x4+4+4 |
RESULT="\( -background black +clone -shadow 50x2+4+4 -background none \) +swap -flatten -trim +repage " |
} |
# Apply a polaroid-like border effect |
# Based on filt_photoframe(), with a bigger lower border |
# filt_polaroid($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_polaroid() { |
trace $@ |
local border=$(( ($3*$4) / 3600 )) # Read filt_photoframe for details |
[[ $border -lt 7 ]] || border=6 |
RESULT="\( -fill white -background white " |
RESULT+=" -bordercolor white -mattecolor white -frame ${border}x${border} " |
# XXX: Double-flipping, there's surely a better way |
RESULT+=" \( -flip -splice 0x$(( $border*5 )) \) " |
RESULT+=" -flip -bordercolor grey60 -border 1 +repage " |
RESULT+="\)" |
} |
# Applies a random rotation |
# Taken from <http://www.imagemagick.org/Usage/thumbnails/#polaroid> |
# filt_randrot($1 = filename, $2 = timestamp, $3 = width, $4 = height) |
filt_randrot() { |
trace $@ |
# Rotation angle [-18..18] |
local angle=$(( ($(rand) % 37) - 18 )) |
RESULT="-background none -rotate $angle " |
} |
# Create the sprocket-holes pattern |
# init_filt_film($1 = capture_width, $2 = capture_height) |
init_filt_film() { |
trace $@ |
[[ -z $FILMSTRIP ]] || return 0 |
local w=$1 h=$2 |
# Base reel dimensions |
#local rw=$(rmultiply $w,0.08) # 8% width |
local rw=51 |
local rh=29 |
local vspad=10 # Vertical padding between sprocket holes |
# Temporary files |
local reel_strip=$(new_temp_file -reel.png) |
local sprocket_mask=$(new_temp_file -smask.png) |
local sprocket=$(new_temp_file -sprocket.png) |
# Create the film reel pattern... |
local rw2=$(( $rw - 10 )) rh2=$(( $rh - 10 )) |
# Instead, create a big enough strip and then resize |
local must_rescale=0 |
if [[ ( $w -lt 240 ) || ( $h -lt 240 ) ]]; then |
must_rescale=1 |
fi |
# I (still) don't know how to do it in a single step, moving the mask to |
# a parenthesised expression won't work, probably due to -alpha interactions |
# First step: Create a mask: Black border, rounded-corners transparent rectangle |
# (Source: http://www.imagemagick.org/Usage/thumbnails/#rounded) |
local r=4 # 8 -> much more rounded, still mostly rectangular |
convert -size ${rw2}x${rh2} 'xc:black' \ |
\( +clone -alpha extract \ |
-draw "fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0" \ |
\( +clone -flip \) -compose Multiply -composite \ |
\( +clone -flop \) -compose Multiply -composite \ |
\) -alpha off -compose CopyOpacity -composite \ |
"$sprocket_mask" |
# Second step: Create a bigger rectangle and cut-out the mask above |
convert -size ${rw}x$(( ${rh} + ${vspad} )) 'xc:white' -gravity Center \ |
"$sprocket_mask" -composite -alpha Copy -negate \ |
"$sprocket" |
if [[ $must_rescale -eq 1 ]]; then |
rws=$(( $(rmultiply $w,0.08) )) |
rhs=$(( ( $rws * 4 ) / 7 )) |
convert "$sprocket" -geometry ${rws}x${rhs} "$sprocket" |
rh=$rhs |
fi |
# FIXME: Error handling |
# Repeat it until the height is reached and crop to the exact height |
local repeat=$( ceilmultiply $h/$rh ) |
let 'repeat += 1' |
#$(yes -- '-clone 0 ( -size 1x5 xc:black ) ' | head -n $repeat) \ |
#-append -crop ${rw}x${h}+0+0 \ |
# Can't use "yes -- '-clone 0'" outside GNU |
convert -background black -fill black "$sprocket" \ |
$(yes 'clone 0' | head -$repeat | sed 's/^/-/') \ |
-append \ |
"$reel_strip" |
FILMSTRIP=$reel_strip |
FILMSTRIP_HOLE_HEIGHT=$(imh "$sprocket") |
} |
# This one requires much more work, the results are pretty rough, but ok as |
# a starting point / proof of concept |
filt_film() { |
trace $@ |
local file="$1" ts=$2 w=$3 h=$4 |
init_filt_film $w $h |
assert "[[ -n '$FILMSTRIP' ]]" |
local skew=$(( $RANDOM % $FILMSTRIP_HOLE_HEIGHT )) |
# As this options will be appended to the commandline we cannot |
# order the arguments optimally (eg: reel.png image.png reel.png +append) |
# A bit of trickery must be done flipping the image. Note also that the |
# second strip will be appended flipped, which is intended. |
RESULT=" \( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
RESULT+="\( '$FILMSTRIP' -crop x${h}+0+$skew \) +append -flop " |
} |
# Creates a contact sheet by calling the delegate |
# create_contact_sheet($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5...$# = vidcaps) : output |
create_contact_sheet() { |
trace $@ |
$CSHEET_DELEGATE "$@" |
} |
# This is the standard contact sheet creator |
# csheet_montage($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = vidcaps) : output |
csheet_montage() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 output=$(new_temp_file .png) |
shift 4 |
# Padding is no longer dependant upong context since alignment of the |
# captures was far trickier then |
local hpad= vpad= splice= |
# The shadows already add a good amount of padding |
if has_filter filt_softshadow ; then |
hpad=0 |
vpad=0 |
splice=5x10 |
else |
hpad=$PADDING |
vpad=$PADDING |
splice=0x8 |
fi |
montage -background Transparent "$@" -geometry +$hpad+$vpad -tile "$cols"x "$output" |
convert "$output" -background Transparent -splice $splice "$output" |
# FIXME: Error handling |
echo $output |
} |
# Polaroid contact sheet creator: it overlaps vidcaps with some randomness |
# csheet_overlap($1 = columns, $2 = context, $3 = width, $4 = height, |
# $5... = $vidcaps) : output |
csheet_overlap() { |
trace $@ |
local cols=$1 ctx=$2 width=$3 height=$4 |
# globals: $VID |
shift 4 |
# TBD: Handle context |
# Explanation of how this works: |
# On the first loop we do what the "montage" command would do (arrange the |
# images in a grid) but overlapping each image to the one on their left, |
# creating the output row by row, each row in a file. |
# On the second loop we append the rows, again overlapping each one to the |
# one before (above) it. |
# XXX: Compositing over huge images is quite slow, there's probably a |
# better way to do it |
# Offset bounds, this controls how much of each snap will be over the |
# previous one. Note it is important to work over $width and not $VID[$W] |
# to cover all possibilities (extended mode and -H change the vidcap size) |
local maxoffset=$(( $width / 3 )) |
local minoffset=$(( $width / 6 )) |
# Holds the files that will form the full contact sheet |
# each file is a row on the final composition |
local -a rowfiles |
# Dimensions of the canvas for each row, it should be big enough |
# to hold all snaps. |
# My trigonometry is pretty rusty but considering we restrict the angle a lot |
# I believe no image should ever be wider/taller than the diagonal (note the |
# ceilmultiply is there to simply round the result) |
local diagonal=$(ceilmultiply $(pyth_th $width $height) 1) |
# XXX: The width, though isn't guaranteed (e.g. using filt_film it ends wider) |
# adding 3% to the diagonal *should* be enough to compensate |
local canvasw=$(( ( $diagonal + $(rmultiply $diagonal,0.3) ) * $cols )) |
local canvash=$(( $diagonal )) |
# The number of rows required to hold all the snaps |
local numrows=$(ceilmultiply ${#@},1/$cols) # rounded division |
# Variables inside the loop |
local col # Current column |
local rowfile # Holds the row we're working on |
local offset # Random offset of the current snap [$minoffset..$maxoffset] |
local accoffset # The absolute (horizontal) offset used on the next iteration |
local cmdopts # Holds the arguments passed to convert to compose the sheet |
local w # Width of the current snap |
for row in $(seqr 1 $numrows) ; do |
col=0 |
rowfile=$(new_temp_file .png) |
rowfiles=( "${rowfiles[@]}" "$rowfile" ) |
accoffset=0 |
cmdopts= # This command is pretty time-consuming, let's make it in a row |
# Base canvas # Integrated in the row creation since 1.0.99 |
# Step through vidcaps (col=[0..cols-1]) |
for col in $(seqr 0 $(( $cols - 1 ))); do |
# More cols than files in the last iteration (e.g. -n10 -c4) |
if [[ -z $1 ]]; then break; fi |
w=$(imw "$1") |
# Stick the vicap in the canvas |
cmdopts="$cmdopts '$1' -geometry +${accoffset}+0 -composite " |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
let 'accoffset=accoffset + w - offset' |
shift |
done |
inf "Composing overlapped row $row/$numrows..." |
eval convert -size ${canvasw}x${canvash} xc:transparent -geometry +0+0 "$cmdopts" -trim +repage "'$rowfile'" >&2 |
done |
inf "Merging overlapped rows..." |
output=$(new_temp_file .png) |
cmdopts= |
accoffset=0 |
local h |
for row in "${rowfiles[@]}" ; do |
w=$(imw "$row") |
h=$(imh "$row") |
minoffset=$(( $h / 8 )) |
maxoffset=$(( $h / 4 )) |
offset=$(( $minoffset + ( $(rand) % $maxoffset ) )) |
# The row is also offset horizontally |
cmdopts="$cmdopts '$row' -geometry +$(( $(rand) % $maxoffset ))+$accoffset -composite " |
let 'accoffset=accoffset + h - offset' |
done |
# After the trim the image will be touching the outer borders and the heading and footer, |
# older versions (prior to 1.0.99) used -splice 0x10 to correct the heading spacing, 1.0.99 |
# onwards uses -frame to add spacing in all borders + splice to add a bit more space on the |
# upper border. Note splice uses the background colour while frame uses the matte colour |
eval convert -size ${canvasw}x$(( $canvash * $cols )) xc:transparent -geometry +0+0 \ |
$cmdopts -trim +repage -bordercolor Transparent -background Transparent -mattecolor Transparent \ |
-frame 5x5 -splice 0x5 "$output" >&2 |
# FIXME: Error handling |
echo $output |
} |
# Sorts timestamps and removes duplicates |
# clean_timestamps($1 = space separated timestamps) |
clean_timestamps() { |
trace $@ |
# Note sort works on lines, hence the stonl |
local s=$1 |
echo "$s" | stonl | sort -n | uniq |
} |
# Test the video at a given timestamp (to see if it can be reached) |
# See safe_length_measure() |
# probe_video($1 = input file, $2 = stamp) |
probe_video() { |
local f="$1" |
local ts="$2" |
local ret=0 |
# This time a resize filter is applied to the player to produce smaller |
# output |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
if ! mplayer_probe "$f" "$ts"; then |
ret=1 |
fi |
elif [[ $DECODER -eq $DEC_FFMPEG ]]; then |
if ! ffmpeg_probe "$f" "$ts" ; then |
ret=1 |
fi |
else |
assert false |
ret=1 |
fi |
return $ret |
} |
# Try to guess a correct length for the video, taking the reported length as a |
# starting point |
# safe_length_measure($1 = filename) |
safe_length_measure() { |
trace $@ |
local f="$1" |
local len=${VID[$LEN]} |
local tempfile= |
local newlen=$len |
local capturefn= |
if probe_video "$1" $len ; then |
inf " File looks fine, suspicion withdrawn" |
echo "$len" |
return 0 |
else |
# Can't seek to the very end, adjust |
warn "Starting safe length measuring (this might take a while)..." |
local maxrew=$(min $QUIRKS_MAX_REWIND $(awkex "int($len)")) # At most we'll rewind 20 seconds |
# -1 (-WS) => Rewind up to the start |
# Might be -2, -4, ... e.g. (-WS -Ws) |
if fptest $maxrew -ge $len || fptest "$maxrew" -lt 0 ; then |
maxrew=$len |
INTERNAL_MAXREWIND_REACHED=1 |
fi |
for rew in $(seqr $QUIRKS_LEN_STEP $maxrew $QUIRKS_LEN_STEP); do |
newlen=$(keepdecimals_lower $(awkexf "$len - $rew") 3) |
warn " ... trying $(pretty_stamp $newlen)" |
if probe_video "$f" "$newlen" ; then |
echo $newlen |
return 0 |
fi |
done |
fi |
# Hitting this line means we're doomed! |
return 1 |
} |
##### {{{{ Codec names |
# Codecs TODO: Clean this |
# Translates an mplayer codec id/fourcc to its name |
get_vcodec_name() { |
local vcid="$1" |
local vcodec= |
# Video codec "prettyfication", see [[R2]], [[R3]], [[R4]] |
case "$vcid" in |
0x10000001) vcodec="MPEG-1" ;; |
0x10000002) vcodec="MPEG-2" ;; |
0x00000000) vcodec="Raw video" ;; # How correct is this? |
# H264 is used in mov/mp4. |
# 0x07 was seen in mplayer 1.0rc2-4.2.1 (FreeBSD) |
0x00000007|avc1|H264) vcodec="MPEG-4 AVC" ;; |
DIV3) vcodec="DivX ;-) Low-Motion" ;; # Technically same as mp43 |
DX50) vcodec="DivX 5" ;; |
FMP4) vcodec="FFmpeg" ;; # XXX: Would LAVC be a better name? |
I420) vcodec="Raw I420 Video" ;; # XXX: Officially I420 is Indeo 4 but it is mapped to raw ¿? |
MJPG) vcodec="M-JPEG" ;; # mJPG != MJPG |
MPG4) vcodec="MS MPEG-4 V1" ;; |
MP42) vcodec="MS MPEG-4 V2" ;; |
MP43) vcodec="MS MPEG-4 V3" ;; |
RV10) vcodec="RealVideo 1.0/5.0" ;; |
RV20) vcodec="RealVideo G2" ;; |
RV30) vcodec="RealVideo 8" ;; |
RV40) vcodec="RealVideo 9/10" ;; |
SVQ1) vcodec="Sorenson Video 1" ;; |
SVQ3) vcodec="Sorenson Video 3" ;; |
theo) vcodec="Ogg Theora" ;; |
tscc) vcodec="TechSmith SCC" ;; |
VP6[012F]) vcodec="On2 Truemotion VP6" ;; |
VP80) vcodec="VP8" ;; |
WMV1) vcodec="WMV7" ;; |
WMV2) vcodec="WMV8" ;; |
WMV3) vcodec="WMV9" ;; |
WMVA) vcodec="WMV9 Advanced Profile" ;; # Not VC1 compliant. Deprecated by Microsoft. |
XVID) vcodec="Xvid" ;; |
3IV2) vcodec="3ivx Delta 4.0" ;; # Rare but seen |
FLV1) vcodec="Sorenson Spark (FLV1)" ;; |
FPS1) vcodec="Fraps" ;; |
# These are known FourCCs that I haven't tested against so far |
WVC1) vcodec="VC-1" ;; |
DIV4) vcodec="DivX ;-) Fast-Motion" ;; |
DIVX|divx) vcodec="DivX" ;; # OpenDivX / DivX 5(?) / Project Mayo |
IV4[0-9]) vcodec="Indeo Video 4" ;; |
IV50) vcodec="Indeo 5.0" ;; |
VP3[01]) vcodec="On2 VP3" ;; |
VP40) vcodec="On2 VP4" ;; |
VP50) vcodec="On2 VP5" ;; |
s263) vcodec="H.263" ;; # 3GPP |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
IV3[0-9]) vcodec="Indeo Video 3" ;; # FF only recognises IV31 |
MSVC) vcodec="Microsoft Video 1" ;; |
MRLE) vcodec="Microsoft RLE" ;; |
3IV1) vcodec="3ivx Delta" ;; |
# "mp4v" is the MPEG-4 fourcc *in mov/mp4/3gp*; but I also found MP4V (Apple's iTunes sample) |
mp4v|MP4V) vcodec="MPEG-4" ;; |
# Synthetic, used for ffmpeg translations |
vcs_divx) vcodec="DivX ;-)" ;; |
*) # If not recognized fall back to FourCC |
vcodec="$vcid" |
;; |
esac |
echo "$vcodec" |
} |
# Translates an FFmpeg codec id to an MPlayer codec id/fourcc |
# TODO: Clean this |
translate_ffmpeg_vcodec_id() { |
# The list of ffmpeg codecs might be retrieved by looking at the code but I |
#+simply used the ffmpeg -formats / ffmpeg -codecs command |
# Supported video decoders: $ ffmepg -codecs | grep '^ D.V' |
local vcid="$1" |
local mpid= |
case "$vcid" in |
mpeg1video) mpid="0x10000001" ;; # mpeg1video_vdpau? |
mpeg2video) mpid="0x10000002" ;; |
rawvideo) mpid="0x00000000" ;; # can't distinguish from I420 |
h264) mpid="avc1" ;; |
mjpeg) mpid="MJPG" ;; |
msmpeg4v1) mpid="MPG4" ;; |
msmpeg4v2) mpid="MP42" ;; |
theora) mpid="theo" ;; |
camtasia) mpid="tscc" ;; |
vp6|vp6a|vp6f) mpid="VP60" ;; |
vp8) mpid="VP80" ;; |
# TODO List of codec id's I translate but haven't tested: |
#+ svq3, rv40, theora, camtasia, vp6* |
# MPlayer uses uppercase whereas FFmpeg uses lowercase |
rv10|rv20|rv30|rv40|svq1|svq3|wmv1|wmv2|wmv3) mpid=$(echo $vcid | tr a-z A-Z) ;; |
# FFmpeg doesn't print FourCC's so there's some codecs that can't be told apart |
msmpeg4) mpid="vcs_divx" ;; # DIV3 = DIV4 = MP43 |
# XVID = DIVX = DX50 = FMP4 = ... = mpeg4 |
mpeg4) mpid="mp4v" ;; # Take advantage of an unamed MPEG-4 |
h263) mpid="s263" ;; |
vc1) mpid="WVC1" ;; # In FF: WMVA = vc1 |
flv) mpid="FLV1" ;; |
fraps) mpid="FPS1" ;; |
# Not supported (ff just prints the FourCC) |
# IV4*, vp4 |
vp3) mpid="VP30" ;; |
vp5) mpid="VP50" ;; |
# Legacy(-er) codecs (haven't seen files in these formats in awhile) |
# MSVC? MRLE? |
indeo3) mpid="IV31" ;; |
*) # If not recognized fall back to FourCC |
mpid="$vcid" |
;; |
esac |
echo $mpid |
} |
get_acodec_name() { |
local acid="$1" |
local acodec= |
local ERE='[ -]' |
if [[ $acid =~ $ERE ]]; then |
# Won't be recognised anyway |
echo "$acid" |
return |
fi |
case "$(tolower "$acid")" in |
85) acodec='MPEG Layer III (MP3)' ;; |
80) acodec='MPEG Layer I/II (MP1/MP2)' ;; # Apparently they use the same tag |
mp4a) acodec='MPEG-4 AAC' ;; # LC and HE, apparently |
352) acodec='WMA7' ;; # =WMA1 |
353) acodec='WMA8' ;; # =WMA2 No idea if lossless can be detected |
354) acodec='WMA9' ;; # =WMA3 |
8192) acodec='AC3' ;; |
1|65534) |
# 1 is standard PCM (apparently all sample sizes) |
# 65534 seems to be multichannel PCM |
acodec='Linear PCM' ;; |
vrbs|22127) |
# 22127 = Vorbis in AVI (with ffmpeg). DON'T! |
# vrbs = Vorbis in Matroska, Ogg, probably others |
acodec='Vorbis' |
;; |
qdm2) acodec="QDesign" ;; |
"") acodec="no audio" ;; |
samr) acodec="AMR" ;; # AMR-NB/AMR-WB? |
# Following not seen by me so far, don't even know if mplayer would |
# identify them |
#<http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2005-November/005054.html> |
355) acodec="WMA9 Lossless" ;; |
10) acodec="WMA9 Voice" ;; |
# Other versions of R.A. listed at Wikipedia/RealAudio |
sipr) acodec="RealAudio SIPR" ;; # RA 4/5 |
cook) acodec="RealAudio Cook" ;; # RA 6 |
*) # If not recognized show audio id tag |
acodec="$acid" |
;; |
esac |
echo "$acodec" |
} |
translate_ffmpeg_acodec_id() { |
local acid="$1" |
local mpid= |
# ffmpeg -codecs | grep ^\ D.A |
case "$acid" in |
mp3) mpid='85' ;; |
# Note FF can tell apart mp1/mp2 directly |
mp1) mpid='MPEG Layer I (MP1)' ;; |
mp2) mpid='MPEG Layer II (MP2)' ;; |
aac) mpid='mp4a' ;; # Can aac be MPEG2? |
wmav1) mpid='352' ;; |
wmav2) mpid='353' ;; |
wmapro) mpid='354' ;; # Actually WMA9 Professional |
ac3) mpid='8192' ;; |
# FF has a ton of pcm variants (sign, endianness, ...) |
pcm_*) mpid="1" ;; |
vorbis) mpid="vrbs" ;; |
qdm2) mpid="QDM2" ;; |
libopencore_amrnb) mpid="AMR-NB" ;; |
libopencore_amrwb) mpid="AMR-WB" ;; |
*) # If not recognized show audio id tag |
mpid="$acid" |
;; |
esac |
echo "$mpid" |
} |
##### }}}} # Codec names |
### {{{ Modularisation/abstraction of video capturers, TODO: work in progress |
check_avail_tools() { |
local capturer='' identifier='' fn= |
for capturer in ${CAPTURERS[*]}; do |
fn=${capturer}_test_avail |
is_defined $fn || continue |
if $fn ; then |
CAPTURERS_AVAIL=( "${CAPTURERS_AVAIL[@]}" "$capturer" ) |
fi |
done |
for identifier in ${IDENTIFIERS[*]}; do |
fn=${identifier}_test_avail |
is_defined $fn || continue |
if $fn ; then |
IDENTIFIERS_AVAIL=( "${IDENTIFIERS_AVAIL[@]}" $identifier ) |
fi |
done |
CAPTURER=${CAPTURERS_AVAIL[0]} |
IDENTIFIER=${IDENTIFIERS_AVAIL[0]} |
if [[ ( -z $CAPTURER ) || ( -z $IDENTIFIER ) ]]; then |
error "No supported video tools (mplayer, ffmpeg) available" |
return $EX_UNAVAILABLE |
fi |
} |
pick_tools() { |
trace $@ |
# User *wants* a certain decoder |
if [[ $USR_CAPTURER ]]; then |
if ! grep -qi "$CAPTURER" <<<"${CAPTURERS_AVAIL[@]}" ; then |
error "User selected capturing tool ($CAPTURER) is not available" |
return $EX_UNAVAILABLE |
fi |
fi |
# DVD mode is optional, and since 1.12 DVD mode can work with multiple inputs too |
# DVD Mode only works with mplayer, the decoder is changed when |
# the DVD mode option is found, so if it's ffmpeg at this point, |
# it's by user request (i.e. -F after -V) |
if [[ $DVD_MODE -eq 1 ]] && ! is_defined "${CAPTURER}_dvd_capture" ; then |
# Pick the first available dvd capturer, if any |
CAPTURER= |
local c= |
for c in "${CAPTURERS_AVAIL[@]}"; do |
if is_defined "${c}_dvd_capture" ; then |
CAPTURER="$c" |
break; |
fi |
done |
if [[ -z $CAPTURER ]]; then |
# None available with DVD support |
error "No available capturer has DVD support" |
return $EX_UNAVAILABLE |
fi |
if [[ $USR_CAPTURER != $CAPTURER ]]; then |
# User choose one, we can't use |
warn "$(tolower $USR_CAPTURER) can't capture in DVD mode, switching to $CAPTURER" |
fi |
fi |
# Propagate to the related settings |
local actual=$CAPTURER |
[[ -z $USR_CAPTURER ]] || set_capturer $USR_CAPTURER 1 # Preferred |
set_capturer $actual 0 # Actual |
} |
### }}} |
# Classic identification, uses mplayer and ffmpeg |
# Use the available tools to identify video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# classic_identify($1 = file) |
classic_identify() { |
trace $@ |
local RET_NOLEN=3 RET_NODIM=4 |
assert '[[ $MPLAYER_BIN && $FFMPEG_BIN ]]' |
assert 'is_defined mplayer_identify && is_defined ffmpeg_identify' |
mplayer_identify "$1" 2>/dev/null |
# ffmpeg_identify in DVD mode only works when the DVD is mounted: |
[[ ( $DVD_MODE -eq 0 ) && ( $FFMPEG_BIN ) ]] && ffmpeg_identify "$1" |
[[ ( $DVD_MODE -eq 1 ) && ( $FFMPEG_BIN ) && ( $DVD_MOUNTP ) ]] && ffmpeg_identify "$1" |
local fid=( "${FFMPEG_ID[@]}" ) |
# Fail early if none detected length |
[[ ( -z ${MPLAYER_ID[$LEN]} ) && ( -z ${FFMPEG_ID[$LEN]} ) ]] && return $RET_NOLEN |
# By default take mplayer's values |
VID=( "${MPLAYER_ID[@]}" ) |
# FFmpeg seems better at getting the correct number of FPS, specially with |
# WMVs, where mplayer often accepts 1000fps while ffmpeg notices the |
# inconsistency in container vs codec and guesses better, *but* it only |
# uses two decimals so 23.976 becomes 23.98. So it is only used when |
# the number of decimals seems right. |
# When a "Seems..." line is printed the correct FPS can be obtained though. |
[[ -z ${MPLAYER_ID[$FPS]} ]] && VID[$FPS]=${fid[$FPS]} |
[[ ${MPLAYER_ID[$FPS]} && ${fid[$FPS]} ]] && { |
# Trust ffmpeg if it has three decimals OR if mplayer is probably-wrong |
local ffps=${fid[$FPS]} |
local ERE='\.[0-9][0-9][0-9]' |
if [[ $ffps =~ $ERE ]]; then |
VID[$FPS]=$ffps |
elif fptest "${MPLAYER_ID[$FPS]}" -gt 500; then |
VID[$FPS]=$ffps |
fi |
} |
# It doesn't appear to need any workarounds for num. channels either |
[[ ${fid[$CHANS]} ]] && VID[$CHANS]=${fid[$CHANS]} |
[[ ${fid[$ASPECT]} ]] && VID[$ASPECT]=${fid[$ASPECT]} |
# There's a huge inconsistency with some files, both mplayer vs ffmpeg |
# same application on different OSes |
local fflen=${fid[$LEN]} mplen=${MPLAYER_ID[$LEN]} # Shorthands |
[[ -z $fflen ]] && fflen=0 |
# If both report 0, there's no good value... |
fptest "$fflen" -eq 0 && fptest "$mplen" -eq 0 && return $RET_NOLEN |
if [[ ( $DVD_MODE -eq 0 ) && ( $QUIRKS -eq 0 ) ]]; then # In DVD mode ffmpeg has no length |
# Quirks disabled, should be enabled? |
local delta=$(abs $(awkexf "($fflen - $mplen)")) |
# If they don't agree, take the shorter as a starting point, |
#+if both are different than zero take min, if one of them is 0, take max to start |
if fptest "$fflen" -ne 0 && fptest "$mplen" -ne 0 ; then |
VID[$LEN]=$(min $fflen $mplen) |
else |
VID[$LEN]=$(max $fflen $mplen) |
delta=$QUIRKS_LEN_THRESHOLD # Ensure it's considered inconsistent |
fi |
# If they differ too much, enter safe mode. If one reports 0, they'll differ... |
# FIXME: If $DECODER reports 0, can it seek?? |
if fptest "$delta" -ge $QUIRKS_LEN_THRESHOLD ; then |
warn "Found inconsistency in reported length. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
# Ensure sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
# Mplayer can identify video as 0x0 |
if [[ ${VID[$W]} -eq 0 ]]; then |
VID[$W]=${FFMPEG_ID[$W]} |
fi |
if [[ ${VID[$H]} -eq 0 ]]; then |
VID[$H]=${FFMPEG_ID[$H]} |
fi |
is_number "${VID[$W]}" && is_number "${VID[$H]}" || return $RET_NODIM |
[[ ${VID[$W]} -gt 0 ]] && [[ ${VID[$H]} -gt 0 ]] || return $RET_NODIM |
# FPS at least with two decimals |
if [[ $(awkex "int(${VID[$FPS]})") == "${VID[$FPS]}" ]]; then |
VID[$FPS]="${VID[$FPS]}.00" |
fi |
local mfps="${MPLAYER_ID[$FPS]}" |
if [[ ( $QUIRKS -eq 0 ) && ( -n $MPLAYER_BIN ) ]] && fptest "$mfps" -eq 1000 ; then |
warn "Suspect file. Safe measuring enabled." |
QUIRKS=1 |
fi |
# Last safeguard: Try to reach the detected length, if it fails, trigger |
# quirks mode |
if [[ $QUIRKS -eq 0 ]]; then |
if ! probe_video "$1" "${VID[$LEN]}" ; then |
warn "Detected video length can't be reached. Safe measuring enabled." |
QUIRKS=1 |
fi |
fi |
if [[ $QUIRKS -eq 1 ]]; then |
VID[$LEN]=$(safe_length_measure "$1") |
if [[ -z ${VID[$LEN]} ]]; then |
error "Couldn't measure length in a reasonable amount of tries." |
if [[ $INTERNAL_MAXREWIND_REACHED -eq 1 ]]; then |
error " Will not be able to capture this file with the current settings." |
else |
local reqs=$(( $INTERNAL_WS_C + 1 )) reqp='' |
[[ $reqs -eq 1 ]] && reqp=" -WP" || reqp=" -WP$reqs" |
[[ $reqs -ge 3 ]] && reqs=" -WS" || { # Third try => Recommend -WS |
[[ $reqs -eq 1 ]] && reqs=" -Ws" || reqs=" -Ws$reqs" |
} |
assert 'fptest "$QUIRKS_MAX_REWIND" -gt 0' |
local offby=$(pretty_stamp $QUIRKS_MAX_REWIND) |
warn " Capturing won't work, video is at least $offby shorter than reported." |
local dname='ffmpeg' |
[[ $DECODER -eq $DEC_MPLAYER ]] && dname='mplayer' |
warn " Does $dname support ${VID[$VCODEC]}?." |
warn " Try re-running with$reqs$reqp." |
fi |
return 1 |
fi |
elif [[ $QUIRKS -eq -2 ]]; then |
warn "Safe mode disabled." |
fi |
# Re-check sanity of the most important values |
is_float "${VID[$LEN]}" || return $RET_NOLEN |
RESULT=( "${VID[@]}" ) |
} |
# Use the selected identifier to extract video meta-data |
# fills $VID with the values |
# Return codes: |
# 3: Failed to detect length |
# 4: Failed to detect width or height |
# identify_video($1 = file) |
identify_video() { |
${IDENTIFIER}_identify "$1" |
VID=( "${RESULT[@]}" ) |
} |
dump_idinfo() { |
trace $@ |
[[ $MPLAYER_BIN ]] && echo "Mplayer: $MPLAYER_BIN" |
[[ $FFMPEG_BIN ]] && echo "FFmpeg: $FFMPEG_BIN" |
[[ $MPLAYER_BIN ]] && cat <<-EODUMP |
=========== Mplayer Identification =========== |
Length: $(pretty_stamp ${MPLAYER_ID[$LEN]}) |
Video |
Codec: ${MPLAYER_ID[$VCODEC]} (${MPLAYER_ID[$VCNAME]}) |
Dimensions: ${MPLAYER_ID[$W]}x${MPLAYER_ID[$H]} |
FPS: ${MPLAYER_ID[$FPS]} |
Aspect: ${MPLAYER_ID[$ASPECT]} |
Audio |
Codec: ${MPLAYER_ID[$ACODEC]} (${MPLAYER_ID[$ACNAME]}) |
Channels: ${MPLAYER_ID[$CHANS]} |
============================================== |
EODUMP |
local ffl="${FFMPEG_ID[$LEN]}" |
[[ $ffl ]] && ffl=$(pretty_stamp "$ffl") |
if [[ ( -z $ffl ) && ( $DVD_MODE -eq 1 ) ]]; then |
ffl="(unavailable in DVD mode)" |
fi |
[[ $FFMPEG_BIN ]] && cat <<-EODUMP |
=========== FFmpeg Identification =========== |
Length: $ffl |
Video |
Codec: ${FFMPEG_ID[$VCODEC]} (${FFMPEG_ID[$VCNAME]}) |
Dimensions: ${FFMPEG_ID[$W]}x${FFMPEG_ID[$H]} |
FPS: ${FFMPEG_ID[$FPS]} |
Aspect: ${FFMPEG_ID[$ASPECT]} |
Audio |
Codec: ${FFMPEG_ID[$ACODEC]} (${FFMPEG_ID[$ACNAME]}) |
Channels: ${FFMPEG_ID[$CHANS]} |
============================================= |
EODUMP |
local xar= |
if [[ ${VID[$ASPECT]} ]]; then |
xar=$(keepdecimals "${VID[$ASPECT]}" 4) |
[[ $xar ]] && xar=" ($xar)" |
fi |
cat <<-EODUMP |
=========== Combined Identification =========== |
Length: $(pretty_stamp ${VID[$LEN]}) |
Video |
Codec: ${VID[$VCODEC]} (${VID[$VCNAME]}) |
Dimensions: ${VID[$W]}x${VID[$H]} |
FPS: ${VID[$FPS]} |
Aspect: ${VID[$ASPECT]}$xar |
Audio |
Codec: ${VID[$ACODEC]} (${VID[$ACNAME]}) |
Channels: ${VID[$CHANS]} |
============================================= |
EODUMP |
} |
# Try to pick some font capable of handling non-latin text |
set_extended_font() { |
trace $@ |
# This selection includes japanese fonts |
local candidates=$(identify -list font | grep 'Font: ' | \ |
egrep -io '[a-z-]*(kochi|mincho|sazanami|ipafont)[a-z-]*') |
if [[ -z $candidates ]]; then |
error "Unable to auto-select filename font, please provide one (see -fullhelp)" |
return 1 |
else |
if [[ $DEBUG -eq 1 ]]; then |
local list=$(echo "$candidates" | sed 's/^/ >/g') |
inf "Available non-latin fonts detected:$NL$list" |
fi |
fi |
# Bias towards the Sazanami family |
shopt -s nocasematch |
local ERE='sazanami' |
if [[ $candidates =~ $ERE ]]; then |
NONLATIN_FONT=$(grep -i 'sazanami' <<<"$candidates" | head -1) |
else |
NONLATIN_FONT=$(head -1 <<<"$candidates") |
fi |
shopt -u nocasematch |
} |
# Checks if the provided arguments make sense and are allowed to be used |
#+together. When an incoherence is found, sets some sane values if reasonable |
#+or fails otherwise. |
coherence_check() { |
trace $@ |
# If -m is used then -S must be used |
if [[ ( $MANUAL_MODE -eq 1 ) && ( -z $INITIAL_STAMPS ) ]]; then |
error "You must provide timestamps (-S) when using manual mode (-m)" |
return $EX_USAGE |
fi |
# In case it's 0/0 or 0.0 since they aren't rejected |
if fptest "$EXTENDED_FACTOR" -eq 0 ; then |
EXTENDED_FACTOR=0 |
fi |
if [[ ( $DECODER -eq $DEC_MPLAYER ) && ( -z $MPLAYER_BIN ) ]]; then |
inf "Mplayer not available." |
set_capturer ffmpeg 0 |
elif [[ ( $DECODER -eq $DEC_FFMPEG ) && ( -z $FFMPEG_BIN ) ]]; then |
inf "FFmpeg not available." |
set_capturer mplayer 0 |
fi |
local filter= |
local -a filts=( ) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]] && |
has_filter filt_polaroid && has_filter filt_apply_stamp ; then |
for filter in ${FILTERS_IND[@]} ; do |
if [[ $filter == 'filt_polaroid' ]]; then |
filts=( "${filts[@]}" "$filter" filt_apply_stamp ) |
elif [[ $filter == 'filt_apply_stamp' ]]; then |
continue; |
else |
filts=( "${filts[@]}" $filter ) |
fi |
done |
FILTERS_IND=( "${filts[@]}" ) |
unset filts |
fi |
# The shoftshadow and randrot filters must be in the correct place |
# or they will affect the image incorrectly. |
# Additionally the default filters can be disabled from the command |
# line (with --disable), they're removed from the filter chain here |
local -a filts=( ) end_filts=( ) |
for filter in ${FILTERS_IND[@]} ; do |
case "$filter" in |
filt_softshadow) |
# Note the newer soft shadows code (1.0.99 onwards) behaves slightly |
# differently. On previous versions disabling shadows only affected |
# the montage shadow (but e.g. the polaroid mode preserved them), |
# this is no longer true |
if [[ $DISABLE_SHADOWS -ne 1 ]]; then |
end_filts[100]="filt_softshadow" |
fi |
;; |
filt_apply_stamp) |
if [[ $DISABLE_TIMESTAMPS -ne 1 ]]; then |
filts=( "${filts[@]}" "$filter" ) |
fi |
;; |
filt_randrot) end_filts[200]="filt_randrot" ;; |
*) filts=( "${filts[@]}" "$filter" ) ;; |
esac |
done |
FILTERS_IND=( "${filts[@]}" "${end_filts[@]}" ) |
# Interval=0 == default interval |
fptest "$INTERVAL" -eq 0 && interval=$DEFAULT_INTERVAL |
# If in non-latin mode and no nonlatin font has been picked try to pick one. |
# Should it fail, fallback to latin font. |
if [[ ( $NONLATIN_FILENAMES -eq 1 ) && ( -z $NONLATIN_FONT ) ]]; then |
set_extended_font || { |
# set_extended_font already warns about lack of fonts |
warn " Falling back to latin font" |
NONLATIN_FILENAMES=0 |
NONLATIN_FONT="$FONT_HEADING" |
} |
fi |
sanitise_fonts |
} |
# If the OS hasn't registered TTF fonts with IM, try to use a saner value |
#+*only* for fonts not overridden |
sanitise_fonts() { |
trace $@ |
# Any default font in use? If all of them are overridden, return |
if [[ $USR_FONT_HEADING && $USR_FONT_TITLE && \ |
$USR_FONT_TSTAMPS && $USR_FONT_SIGN ]]; then |
return |
fi |
# If the user edits any font in the script, stop messing with this |
[[ ( -z $USR_FONT_HEADING ) && ( $FONT_HEADING != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TITLE ) && ( $FONT_TITLE != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_TSTAMPS ) && ( $FONT_TSTAMPS != 'DejaVu-Sans-Book' ) ]] && return |
[[ ( -z $USR_FONT_SIGN ) && ( $FONT_SIGN != 'DejaVu-Sans-Book' ) ]] && return |
# Try to locate DejaVu Sans |
local dvs='' |
if [[ -d /usr/local/share/fonts ]]; then |
dvs=$(find /usr/local/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ ( -z $dvs ) && ( -d /usr/share/fonts ) ]]; then |
dvs=$(find /usr/share/fonts/ -type f -iname 'dejavusans.ttf') |
fi |
if [[ -z $dvs ]]; then |
warn "Unable to locate DejaVu Sans font. Falling back to helvetica." |
dvs=helvetica |
fi |
[[ -z $USR_FONT_HEADING ]] && FONT_HEADING="$dvs" |
[[ -z $USR_FONT_TITLE ]] && FONT_TITLE="$dvs" |
[[ -z $USR_FONT_TSTAMPS ]] && FONT_TSTAMPS="$dvs" |
[[ -z $USR_FONT_SIGN ]] && FONT_SIGN="$dvs" |
[[ $DEBUG -eq 1 ]] || { return 0; } |
cat >&2 <<-EOFF |
Font Sanitation: |
font_heading: $FONT_HEADING |
font_title : $FONT_TITLE |
font_tstamps: $FONT_TSTAMPS |
font_sign : $FONT_SIGN |
EOFF |
} |
# Main function. |
# Creates the contact sheet. |
# process($1 = file) |
process() { |
trace $@ |
local f=$1 |
local numcols= |
# Save variables that will be overwritten and must be reset with multiple files |
# pre_* will contain the user-supplied or default values |
local pre_quirks=$QUIRKS |
local pre_aspect_ratio=$ASPECT_RATIO |
local pre_format="$FORMAT" |
INTERNAL_MAXREWIND_REACHED=0 # Reset for each file |
CAPTURES='' |
FILMSTRIP='' # Reset |
DVD_MOUNTP= DVD_TITLE= # Re-set for each file |
if [[ $DVD_MODE -eq 1 ]]; then |
local dvdn=$(realpathr "$f") |
# Is it an ISO? |
if [[ -f $dvdn ]]; then |
DVD_MOUNTP=$(get_dvd_image_mountpoint "$dvdn") |
if [[ -z $DVD_MOUNTP ]]; then |
# Only in Linux does this matter |
if ! is_linux ; then |
warn "Video properties detection for ISO files is not accurate" |
else |
warn "Mount DVD image to get better video properties detection" |
fi |
fi |
else |
# It's a device. Note BSD has no concept of block devices. |
# It MUST be mounted to continue. This is required to allow ffmpeg detection |
#+and to calculate file size |
if ! mount | egrep -q "^$dvdn\ " ; then |
error "DVD mode requires device ($f) to be mounted" |
return $EX_UNAVAILABLE |
fi |
DVD_MOUNTP=$(mount | grep -o "^$dvdn *on [^ ]*" | cut -d' ' -f3) |
dvdn="DVD $f" |
fi |
if [[ ! -r $f ]]; then |
error "Can't access DVD ($f)" |
return $EX_NOINPUT |
fi |
inf "Processing $dvdn..." |
unset dvdn |
DVD_TITLE=${DVD_TITLES[0]} |
DVD_TITLES=( "${DVD_TITLES[@]:1}" ) # shift array |
if [[ ( -z $DVD_TITLE ) || ( $DVD_TITLE == '0' ) ]]; then |
local dt="$(lsdvd "$f" 2>/dev/null | grep 'Longest track:' | \ |
cut -d' ' -f3- | sed 's/^0*//')" |
if ! is_number "$dt" ; then |
error "Failed to autodetect longest DVD title for '$f'" |
exit $EX_INTERNAL |
fi |
DVD_TITLE=$dt |
unset dt |
fi |
DVD_VTS=$(lsdvd -t$DVD_TITLE -v "$f" 2>/dev/null | grep -o 'VTS: [0-9]*' | cut -d' ' -f2) |
inf "Using DVD Title #$DVD_TITLE (VTS: $DVD_VTS) for '$f'" |
else # Not DVD Mode: |
if [[ ! -f $f ]]; then |
error "File \"$f\" doesn't exist" |
return $EX_NOINPUT |
fi |
inf "Processing $f..." |
fi |
create_temp_dir |
# {{SET_E}} Beware, set -e will break this |
identify_video "$f" |
local ecode=$? |
[[ $ecode -eq 0 ]] || { |
case $ecode in |
3) error "Unable to find length of file \"$f\". Can't continue." ;; |
4) error "Unable to detect dimensions of file \"$f\". Can't continue." ;; |
*) error "Failure while analysing file \"$f\". Can't continue." ;; |
esac |
return $EX_UNAVAILABLE |
} |
# Identification-only mode? |
[[ $UNDFLAG_IDONLY ]] && dump_idinfo && return 0 |
# Vidcap/Thumbnail height |
local vidcap_height=$HEIGHT |
if is_percentage "$HEIGHT" && [[ $HEIGHT != '100%' ]]; then |
vidcap_height=$(rpercent ${VID[$H]} ${HEIGHT}) |
inf "Height: $HEIGHT of ${VID[$H]} = $vidcap_height" |
fi |
if ! is_number "$vidcap_height" || [[ $vidcap_height -eq 0 ]]; then |
vidcap_height=${VID[$H]} |
fi |
# -2: DVD Mode autodetection => If ffmpeg/mplayer was unable autodetect, otherwise |
#+ honor detected value |
if [[ $ASPECT_RATIO -eq -2 ]]; then |
[[ ${VID[$ASPECT]} ]] && ASPECT_RATIO=0 || ASPECT_RATIO=-1 |
elif [[ $ASPECT_RATIO -eq 0 ]]; then |
if [[ ${VID[$ASPECT]} ]]; then |
# Aspect ratio in file headers, honor it |
ASPECT_RATIO=$(awkexf "${VID[$ASPECT]}") |
else |
ASPECT_RATIO=$(awkexf "${VID[$W]} / ${VID[$H]}") |
fi |
elif [[ $ASPECT_RATIO -eq -1 ]]; then |
ASPECT_RATIO=$(guess_aspect ${VID[$W]} ${VID[$H]}) |
inf "Aspect ratio set to $ASPECT_RATIO." |
fi |
local vidcap_width=$(compute_width $vidcap_height) |
local nc=$NUMCAPS |
unset TIMECODES |
# Compute the stamps (if in auto mode)... |
if [[ $MANUAL_MODE -eq 1 ]]; then |
# Note TIMECODES must be set as an array to get the correct count in |
# manual mode; in automatic mode it will be set correctly inside |
# compute_timecodes() |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
else |
TIMECODES=( "${INITIAL_STAMPS[@]}" ) |
compute_timecodes $TIMECODE_FROM $INTERVAL $NUMCAPS || { |
return $? |
} |
fi |
local output=$(new_temp_file '-preview.png') |
# If the temporal vidcaps for mplayer already exist, abort |
if [[ $DECODER -eq $DEC_MPLAYER ]]; then |
for f_ in 1 2 3 4 5; do |
if [[ -f "0000000${f_}.png" ]]; then |
error "File 0000000${f_}.png exists and would be overwritten, move it out before running." |
return $EX_CANTCREAT |
fi |
done |
fi |
# Assert sanity of decoder |
assert_if '[[ $DVD_MODE -ne 0 ]]' 'is_defined ${CAPTURER}_dvd_capture' |
assert 'is_defined ${CAPTURER}_capture' |
TEMPSTUFF=( "${TEMPSTUFF[@]}" '00000005.png' ) |
# Highlights |
local hlfile n=1 # hlfile Must be outside the if! |
if [[ $HLTIMECODES ]]; then |
local hlcapfile= pretty= |
local -a capfiles |
for stamp in $(clean_timestamps "${HLTIMECODES[*]}"); do |
if fptest $stamp -gt ${VID[$LEN]} ; then (( ++n )) && continue ; fi |
pretty=$(pretty_stamp $stamp) |
inf "Generating highlight #${n}/${#HLTIMECODES[@]} ($pretty)..." |
hlcapfile=$(new_temp_file "-hl-$(pad 6 $n).png") |
capture "$f" "$hlcapfile" $stamp '1' || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$hlcapfile" $pretty $vidcap_width $vidcap_height $CTX_HL $n || { |
local r=$? |
error "Failed to apply transformations to the capture." |
return $r |
} |
capfiles=( "${capfiles[@]}" "$hlcapfile" ) |
(( ++n )) |
done |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # There's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing highlights contact sheet..." |
hlfile=$( create_contact_sheet $numcols $CTX_HL $vidcap_width $vidcap_height "${capfiles[@]}" ) |
unset hlcapfile pretty n capfiles numcols |
fi |
unset n |
# Normal captures |
local capfile pretty n=1 |
unset capfiles ; local -a capfiles ; local tfile= |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
inf "Generating capture #${n}/${#TIMECODES[*]} ($pretty)..." |
# identified by capture number, padded to 6 characters |
tfile=$(new_temp_file "-cap-$(pad 6 $n).png") |
capture "$f" "$tfile" $stamp $DISABLE_EVASION || return $? |
if [[ $RESULT != "$stamp" ]]; then |
stamp=$RESULT |
pretty=$(pretty_stamp $RESULT) |
fi |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$tfile" $pretty $vidcap_width $vidcap_height $CTX_STD $n || return $? |
capfiles=( "${capfiles[@]}" "$tfile" ) |
(( n++ )) |
done |
#filter_all_vidcaps "${capfiles[@]}" |
assert "[[ '"$n"' -gt 1 ]]" |
(( n-- )) # there's an extra inc |
if [[ $n -lt $COLUMNS ]]; then |
numcols=$n |
else |
numcols=$COLUMNS |
fi |
inf "Composing standard contact sheet..." |
output=$(create_contact_sheet $numcols $CTX_STD $vidcap_width $vidcap_height "${capfiles[@]}") |
unset capfile capfiles pretty n # must carry on to the extended caps: numcols |
# Extended mode |
local extoutput= |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Number of captures. Always rounded to a multiplier of *double* the |
# number of columns (the extended caps are half width, this way they |
# match approx with the standard caps width) |
local hlnc=$(rtomult $(awkex "int(${#TIMECODES[@]} * $EXTENDED_FACTOR)") $((2*numcols))) |
unset TIMECODES # required step to get the right count |
declare -a TIMECODES # Note the manual stamps are not included anymore |
compute_timecodes $TC_NUMCAPS "" $hlnc |
unset hlnc |
local n=1 w= h= capfile= pretty= |
unset capfiles ; local -a capfiles |
# The image size of the extra captures is 1/4, adjusted to compensante the padding |
(( w=vidcap_width/2-PADDING, h=vidcap_height*w/vidcap_width ,1 )) |
assert "[[ ( '"$w"' -gt 0 ) && ( '"$h"' -gt 0 ) ]]" |
for stamp in $(clean_timestamps "${TIMECODES[*]}"); do |
pretty=$(pretty_stamp $stamp) |
capfile=$(new_temp_file "-excap-$(pad 6 $n).png") |
inf "Generating capture from extended set: ${n}/${#TIMECODES[*]} ($pretty)..." |
capture "$f" "$capfile" $stamp $DISABLE_EVASION || return $? |
[[ $CAPTURED_FROM_CACHE -eq 1 ]] ||\ |
filter_vidcap "$capfile" $pretty $w $h $CTX_EXT $n || return $? |
capfiles=( "${capfiles[@]}" "$capfile" ) |
(( n++ )) |
done |
(( n-- )) # There's an extra inc |
if [[ $n -lt 'COLUMNS*2' ]]; then |
numcols=$n |
else |
numcols=$(( $COLUMNS * 2 )) |
fi |
inf "Composing extended contact sheet..." |
extoutput=$( create_contact_sheet $numcols $CTX_EXT $w $h "${capfiles[@]}" ) |
unset w h capfile pretty n numcols |
fi # Extended mode |
local vcodec=${VID[$VCNAME]} |
local acodec=${VID[$ACNAME]} |
if [[ ${VID[$CHANS]} ]] && is_number "${VID[$CHANS]}" && [[ ${VID[$CHANS]} -ne 2 ]]; then |
if [[ ${VID[$CHANS]} -eq 1 ]]; then |
acodec="$acodec (mono)" |
else |
acodec="$acodec (${VID[$CHANS]}ch)" |
fi |
fi |
local csw=$(imw "$output") exw= hlw= |
local width=$csw |
if [[ -n $HLTIMECODES || ( $EXTENDED_FACTOR != '0' ) ]]; then |
inf "Merging contact sheets..." |
if [[ -n $HLTIMECODES ]]; then |
local hlw=$(imw "$hlfile") |
if [[ $hlw -gt $width ]]; then width=$hlw ; fi |
fi |
if [[ $EXTENDED_FACTOR != '0' ]]; then |
local exw=$(imw $extoutput) |
if [[ $exw -gt $width ]]; then width=$exw ; fi |
fi |
fi |
if [[ $csw -lt $width ]]; then |
local csh=$(imh "$output") |
# Expand the standard set to the maximum width of the sets by padding both sides |
# For some reason the more obvious (to me) convert command-lines lose |
# the transparency |
local csw2= ; (( csw2 = (width-csw) / 2 )) |
convert \( -size ${csw2}x$csh xc:transparent \) "$output" \ |
\( -size ${csw2}x$csh xc:transparent \) +append "$output" |
unset csh csw2 |
fi |
# If there were highlights then mix them in |
if [[ $HLTIMECODES ]]; then |
# For some reason adding the background also adds padding with: |
# convert \( -background LightGoldenRod "$hlfile" -flatten \) \ |
# \( "$output" \) -append "$output" |
# replacing it with a "-composite" operation apparently works |
# Expand the highlights to the correct size by padding |
local hlh=$(imh "$hlfile") |
if [[ $hlw -lt $width ]]; then |
local hlw2= ; (( hlw2=(width - hlw) / 2 )) |
convert \( -size ${hlw2}x$hlh xc:transparent \) "$hlfile" \ |
\( -size ${hlw2}x$hlh xc:transparent \) +append "$hlfile" |
unset hlw2 |
fi |
convert \( -size ${width}x${hlh} xc:LightGoldenRod "$hlfile" -composite \) \ |
\( -size ${width}x1 xc:black \) \ |
"$output" -append "$output" |
unset hlh |
fi |
# Extended captures |
if [[ $EXTENDED_FACTOR != 0 ]]; then |
# Already set local exw=$(imw "$extoutput") |
local exh=$(imh "$extoutput") |
if [[ $exw -lt $width ]]; then |
# Expand the extended set to be the correct size |
local exw2= ; (( exw2=(width - exw) / 2 )) |
convert \( -size ${exw2}x$exh xc:transparent \) "$extoutput" \ |
\( -size ${exw2}x$exh xc:transparent \) +append "$extoutput" |
fi |
convert "$output" -background Transparent "$extoutput" -append "$output" |
fi |
# Add the background; -trim added in 1.11. I'm unsure of why but whithout trimmin extra blank |
#+space is added at the top |
local dotrim= |
[[ ( $DISABLE_SHADOWS -eq 1 ) && ( -z $HLTIMECODES ) ]] && dotrim=-trim |
convert -background "$BG_CONTACT" "$output" -flatten $dotrim "$output" |
# Let's add meta inf and signature |
inf "Adding header and footer..." |
local meta2="Dimensions: ${VID[$W]}x${VID[$H]}" |
meta2="$meta2${NL}Format: $vcodec / $acodec${NL}FPS: ${VID[$FPS]}" |
local signature |
if [[ $ANONYMOUS_MODE -eq 0 ]]; then |
signature="$SIGNATURE $USERNAME${NL}with $PROGRAM_SIGNATURE" |
else |
signature="Created with $PROGRAM_SIGNATURE" |
fi |
local headwidth=$(imw "$output") headheight= |
local heading=$(new_temp_file .png) |
# Add the title if any |
if [[ $TITLE ]]; then |
local tlheight=$(line_height "$FONT_TITLE" "$PTS_TITLE") |
convert \ |
\( \ |
-size ${headwidth}x$tlheight "xc:$BG_TITLE" \ |
-font "$FONT_TITLE" -pointsize "$PTS_TITLE" \ |
-background "$BG_TITLE" -fill "$FG_TITLE" \ |
-gravity Center -annotate 0 "$TITLE" \ |
\) \ |
-flatten \ |
"$output" -append "$output" |
unset tlheight |
fi |
local fn_font= # see $NONLATIN_FILENAMES |
if [[ $NONLATIN_FILENAMES -ne 1 ]]; then |
fn_font=$FONT_HEADING |
else |
fn_font=$NONLATIN_FONT |
fi |
# Create a small image to see how tall are characters. In my tests, no matter |
#+which character is used it's always the same height. |
local lineheight=$(line_height "$FONT_HEADING" "$PTS_META") |
# Since filename can be set in a different font check it too |
if [[ $fn_font != "$FONT_HEADING" ]]; then |
local fnlineheight=$(line_height "$fn_font" "$PTS_META") |
[[ $fnlineheight -le $lineheight ]] || lineheight=$fnlineheight |
unset fnlineheight |
fi |
headheight=$(( lineheight * 3 )) |
# Talk about voodoo... feel the power of IM... let's try to explain what's this: |
# It might technically be wrong but it seems to work as I think it should |
# (hence the voodoo I was talking) |
# Parentheses restrict options inside them to only affect what's inside too |
# * Create a base canvas of the desired width and height 1. The width is tweaked |
# because using "label:" later makes the text too close to the border, that |
# will be compensated in the last step. |
# * Create independent intermediate images with each row of information, the |
# filename row is split in two images to allow changing the font, and then |
# they're horizontally appended (and the font reset) |
# * All rows are vertically appended and cropped to regain the width in case |
# the filename is too long |
# * The appended rows are appended to the original canvas, the resulting image |
# contains the left row of information with the full heading width and |
# height, and this is the *new base canvas* |
# * Draw over the new canvas the right row with annotate in one |
# operation, the offset compensates for the extra pixel from the original |
# base canvas. XXX: Using -annotate allows setting alignment but it breaks |
# vertical alignment with the other rows' labels. |
# * Finally add the border that was missing from the initial width, we have |
# now the *complete header* |
# * Add the contact sheet and append it to what we had. |
# * Start a new image and annotate it with the signature, then append it too. |
local filename_label="Filename" |
local filesize_label="File size" |
local filename_value= |
local filesize_value= |
if [[ $DVD_MODE -eq 1 ]]; then |
# lsdvd is guaranteed to be installed if DVD mode is enabled |
local dvd_label=$(lsdvd "$f" 2>/dev/null | grep -o 'Disc Title: .*' | cut -d' ' -f3-) |
# There's no guarantee that titles are on separate VTS, I have no idea |
# how to compute the actual title size |
if [[ $DVD_MOUNTP ]]; then |
filename_label="Disc label" |
filename_value="$dvd_label" |
filesize_label="Titleset size" |
filesize_value="$(get_dvd_size)" |
else |
# Not mounted. We can get the disc size but this will include any other titles. |
# Since 1.11 mounting DVDs is mandatory to get the title size. Both for ISOs and |
#+ devices |
filename_value="$(basename "$f") $filename_value (DVD Label: $dvd_label)" |
is_linux && warn "DVD not mounted: Can't detect title file size." |
filesize_label='Disc image size' |
filesize_value="$(get_pretty_size $(dur "$f"))" |
fi |
else |
filename_value="$(basename "$f")" |
filesize_value="$(get_pretty_file_size "$f")" |
fi |
local signlh=$(line_height "$FONT_SIGN" "$PTS_SIGN") |
local signheight=$(( 4 + ( signlh * 2 ) )) |
convert \ |
\( \ |
-size $(( headwidth - 18 ))x1 "xc:$BG_HEADING" +size \ |
-font "$FONT_HEADING" -pointsize "$PTS_META" \ |
-background "$BG_HEADING" -fill "$FG_HEADING" \ |
\( \ |
-gravity West \ |
\( label:"$filename_label: " \ |
-font "$fn_font" label:"$filename_value" +append \ |
\) \ |
-font "$FONT_HEADING" \ |
label:"$filesize_label: $filesize_value" \ |
label:"Length: $(cut -d'.' -f1 <<<$(pretty_stamp ${VID[$LEN]}))" \ |
-append -crop ${headwidth}x${headheight}+0+0 \ |
\) \ |
-append \ |
\( \ |
-size ${headwidth}x${headheight} \ |
-gravity East -fill "$FG_HEADING" -annotate +0-1 "$meta2" \ |
\) \ |
-bordercolor "$BG_HEADING" -border 9 \ |
\) \ |
"$output" -append \ |
\( \ |
-size ${headwidth}x$signheight -gravity Center "xc:$BG_SIGN" \ |
-font "$FONT_SIGN" -pointsize "$PTS_SIGN" \ |
-fill "$FG_SIGN" -annotate 0 "$signature" \ |
\) \ |
-append \ |
"$output" |
unset signature meta2 headwidth headheight heading fn_font signheight signlh |
local wanted_name=${OUTPUT_FILES[$FILEIDX]} |
if [[ -n $wanted_name ]]; then |
local ERE='\.[^.]+$' |
if [[ $wanted_name =~ $ERE ]]; then |
FORMAT=$(filext "$wanted_name") |
inf "Output format set from output filename" |
else # No file extension in wanted_name |
wanted_name="$wanted_name.$FORMAT" |
fi |
fi |
[[ -n $wanted_name ]] || wanted_name="$(basename "$f").$FORMAT" |
if [[ $FORMAT != 'png' ]]; then |
local newout="$(dirname "$output")/$(basename "$output" .png).$FORMAT" |
convert -quality $QUALITY "$output" "$newout" |
output="$newout" |
fi |
output_name=$( safe_rename "$output" "$wanted_name" ) || { |
error "Failed to write the output file!" |
return $EX_CANTCREAT |
} |
inf "Done. Output wrote to $output_name" |
(( FILEIDX++ ,1 )) #,1 so that it's always ok |
if [[ $UNDFLAG_DISPLAY -eq 1 ]]; then |
if type -pf $UNDFLAG_DISPLAY_COMMAND; then |
$UNDFLAG_DISPLAY_COMMAND "$output_name" |
else |
display "$output_name" |
fi |
fi >/dev/null 2>&1 |
[[ $UNDFLAG_DISCARD -eq 1 ]] && TEMPSTUFF+=( "$output_name" ) |
[[ $UNDFLAG_HANG ]] && read -p 'Main loop paused, hit Enter key to continue... ' |
cleanup |
# Re-set variables (for multi-file input) |
QUIRKS=$pre_quirks |
ASPECT_RATIO=$pre_aspect_ratio |
FORMAT="$pre_format" |
} |
# }}} # Core functionality |
# {{{ # Debugging helpers |
# Tests integrity of some operations. |
# Used to test internal changes for consistency. |
# It helps me to identify incorrect optimizations. |
# internal_integrity_test(). Running with -D triggers this. |
internal_integrity_test() { |
local t op val ret comm retval=0 |
# Replacements |
local SEQ=$(type -pf seq) |
local JOT=$(type -pf jot) |
local ex rex |
if [[ $SEQ ]]; then |
ex=$($SEQ 1 10) |
elif [[ $JOT ]]; then |
ex=$($JOT 10 1) |
else |
warn "Can't check seqr() correctness, neither seq nor jot found" |
fi |
if [[ $ex ]]; then |
exr=$(seqr 1 10) |
if [[ $exr != "$ex" ]]; then |
error "Failed test: seqr() not consistent with external result" |
(( retval++ ,1 )) |
else |
inf "Passed test (seq replacement): consistent result" |
fi |
fi |
# Textual tests, compare output to expected output |
# Tests are in the form "operation arguments correct_result #Description" |
TESTS=( # Note bash2 doesn't like this array as a local variable |
# TODO: UNIX vs GNU |
#"stonl ..." |
"rmultiply 1,1 1 #Identity" |
"rmultiply 16/9,1 2 #Rounding" # 1.77 rounded 2 |
"rmultiply 4/3 1 #Rounding" # 1.33 rounded 1 |
"rmultiply 1,16/9 2 #Commutative property" |
"rmultiply 1.7 2 #Alternate syntax" |
"ceilmultiply 1,1 1 #" |
"ceilmultiply 4/3 2 #" # 1.33 rounded 2 |
"ceilmultiply 7,1/2 4 #" # 3.5 rounded 4 |
"ceilmultiply 7/2 4 #Alternative syntax" |
"ceilmultiply 1/2,7 4 #Commutative property" |
"pad 10 0 0000000000 #Padding" |
"pad 1 20 20 #Unneeded padding" |
"pad 5 23.3 023.3 #Floating point padding" |
"guess_aspect 720 576 4/3 #DVD AR Guess" |
"guess_aspect 1024 576 1024/576 #Unsupported Wide AR Guess" |
"tolower ABC abc #lowercase conversion" |
"pyth_th 4 3 5 #Integer pythagorean theorem" |
#bc result: "pyth_th 16 9 18.35755975068581929849 #FP pythagorean theorem" |
#perl result: "pyth_th 16 9 18.3575597506858 #FP pythagorean theorem" |
"pyth_th 16 9 18.35755975068581946630 #FP pythagorean theorem" |
"get_interval 2h 7200 #Hours parsing" |
"get_interval 2m 120 #Minutes parsing" |
"get_interval 30S 30 #Seconds parsing" |
"get_interval .30 .30 #Milliseconds parsing" |
# Since now the numbers are passed to perl, leading zeroes become octal |
# numbers. Must ensure they are handled correctly |
"get_interval 09h010m09s1 33010 #Parsing with leading zeroes" |
"get_interval 0400 400 #Parsing shorthand" |
# Extended syntax |
"get_interval 30m30m1h 7200 #Repeated minutes parsing" |
) |
for t in "${TESTS[@]}" ; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
# Expected value |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) # Don't use delimiter '/', passed in some $val |
[[ -n $comm ]] || comm=unnamed |
ret=$($op) || true |
if [[ $ret != "$val" ]] && fptest "$ret" -ne "$val" ; then |
error "Failed test ($comm): '$op $val'. Got result '$ret'." |
(( ++retval )) |
else |
inf "Passed test ($comm): '$op $val'." |
fi |
done |
# Returned value tests, compare return to expected return |
TESTS=( |
# Don't use anything with a RE meaning |
# Floating point numeric "test" |
"fptest 3 -eq 3 0 #FP test" |
"fptest 3.2 -gt 1 0 #FP test" |
"fptest 1/2 -le 2/3 0 #FP test" |
"fptest 6.34 -gt 6.34 1 #FP test" |
"fptest (1>0) -eq 1 0 #FP -logical- test" |
"is_number 3 0 #Numeric recognition" |
"is_number '3' 1 #Quoted numeric recognition" |
"is_number 3.3 1 #Non-numeric recognition" |
"is_float 3.33 0 #Float recognition" |
"is_float 3 0 #Float recognition" |
"is_float 1/3 1 #Non-float recognition" |
"is_fraction 1/1 0 #Fraction recognition" |
"is_fraction 1 1 #Non-fraction recognition" |
"is_fraction 1.1 1 #Non-fraction recognition" |
"is_pos_or_percent 33 0 #Positive recognition" |
"is_pos_or_percent 33% 0 #Percent recognition" |
"is_pos_or_percent 4/4% 1 #Percent recognition" |
"is_pos_or_percent % 1 #Percent recognition" |
) |
for t in "${TESTS[@]}"; do |
comm=${t/#*#/} # 's/.*#//' |
t=${t/%#*/} # 's/#.*//' |
val=$(awk '{print $NF}' <<<$t) |
op=$(sed "s! $val *\$!!" <<<$t) |
[[ -n $comm ]] || comm=unnamed |
ret=0 |
$op || { |
ret=$? |
} |
if [[ $val -eq $ret ]]; then |
inf "Passed test ($comm): '$op; returns $val'." |
else |
error "Failed test ($comm): '$op; returns $val'. Returned '$ret'" |
(( retval++ ,1 )) |
fi |
done |
return $retval |
} |
# }}} # Debugging helpers |
# {{{ # Help / Info |
# Prints the program identification to stderr |
show_vcs_info() { # Won't be printed in quiet modes |
# Don't colourise this |
infplain "Video Contact Sheet *NIX v${VERSION}${SUBVERSION}, (c) 2007-2016 Toni Corvera" |
} |
# Prints the list of options to stdout |
# show_help($1 = long = '') |
show_help() { |
local P=$(basename $0) |
local showlong=$1 |
local mpchosen= ffchosen= longhelp= funkyex= |
[[ -z $MPLAYER_BIN ]] && mpchosen=' [Not available]' |
[[ $MPLAYER_BIN && ( $DECODER == $DEC_MPLAYER ) ]] && mpchosen=' [Selected]' |
[[ -z $FFMPEG_BIN ]] && ffchosen=', Not available' |
[[ $FFMPEG_BIN && ( $DECODER == $DEC_FFMPEG ) ]] && ffchosen=', Selected' |
# This portion of help is only shown when in full help mode (--fullhelp) |
[[ $showlong ]] && longhelp=\ |
" --anonymous Disable the 'Preview created by' line in the footer. |
-Ij|-Ik|-Ij=fontname|-Ik=fontname |
--nonlatin Use an alternate font in the heading for the video file |
name. Required to display correctly file names in |
some languages (Chinese, Japanese, Hangul, |
Cyrillic, ...). |
Will try to use a reasonable font. Can also be set |
manually like: |
$ vcs -Ij=Sazanami-Mincho-Regular file.avi |
or |
$ vcs -Ij=/usr/share/fonts/ttf/ttf-japanese-mincho.ttf\\ |
file.avi |
Use \"identify -list font\" to list the available fonts |
-O|--override <arg> Override a variable (see the homepage for more details). |
The accepted format is 'variable=value' (can |
also be quoted -variable=\"some value\"- and can take an |
internal variable too -variable='\$SOME_VAR'-). |
Tweaks and workarounds: |
-Ws Increase length of safe measuring (try harder). Repeat |
to increase further. |
-WS Scan all video, if required, to get a safe measuring. |
-Wp Increase safe measuring precission (i.e. halve the |
probe stepping). Repeat to increase further. |
-WP Inverse of -Wp. |
-Wo Change ffmpeg's arguments order, might work with some |
files that fail otherwise. |
-Wc Disable colour in console messages. |
Obscure options, debugging tools and workarounds: |
-R <file> |
--randomsource <file> Use the provided file as a source for \"random\" values: |
they won't be random anymore, so two runs with the same |
source and same arguments will produce the same output |
in modes which use randomisation (e.g. the |
\"photos\" and \"polaroid\" modes). |
-D Debug mode. Used to test features/integrity. It: |
* Prints the input command line |
* Sets the title to reflect the command line |
* Does a basic test of consistency |
* Prints all internal functions as they are called |
" |
# The --funky help is really long, so make it shorter by default, |
# only show the complete help when --fullhelp is used |
[[ $showlong ]] && funkyex=" |
These are toy output modes in which the contact sheet |
gets a more informal look. |
Order *IS IMPORTANT*. A bad order gets a bad result :P |
Many of these modes are random in nature so using the |
same mode twice will usually lead to different results. |
Currently available \"funky modes\": |
\"overlap\": Use '-ko' or '--funky overlap' |
Randomly overlap captures. |
\"rotate\": Use '-kr' or '--funky rotate' |
Randomly rotate each image. |
\"photoframe\": Use '-kf' or '--funky photoframe' |
Adds a photo-like white frame to each image. |
\"polaroidframe\": Use '-kL' or '--funky polaroidframe' |
Adds a polaroid picture-like white frame to each |
image. |
\"photos\": Use '-kc' or '--funky photos' |
Combination of rotate, photoframe and overlap. |
Same as -kp -kr -ko. |
\"polaroid\": Use '-kp' or '--funky polaroid' |
Combination of rotate, polaroidframe and overlap. |
Same as -kL -kr -ko. |
\"film\": Use '-ki' or '--funky film' |
Imitates filmstrip look. |
\"random\": Use '-kx' or '--funky random' |
Randomises colours and fonts." |
[[ -z $showlong ]] && funkyex=" |
Available: overlap, rotate, photoframe, polaroidframe, |
photos, polaroid, film, random |
Use --fullhelp for more details." |
cat <<EOF |
Usage: $P [options] <file> |
Options: |
-i|--interval <arg> Set the interval to arg. Units can be used |
(case-insensitive), i.e.: |
Seconds: 90 or 90s |
Minutes: 3m |
Hours: 1h |
Combined: 1h3m90 |
Use either -i or -n. |
-n|--numcaps <arg> Set the number of captured images to arg. Use either |
-i or -n. |
-c|--columns <arg> Arrange the output in 'arg' columns. |
-H|--height <arg> Set the output (individual thumbnail) height. Width is |
derived accordingly. Note width cannot be manually set. |
-o|--output <file> File name of output. When ommited will be derived from |
the input filename. Can be repeated for multiple files. |
-a|--aspect <aspect> Aspect ratio. Accepts a floating point number or a |
fraction. |
-f|--from <arg> Set starting time. No caps before this. Same format |
as -i. |
-t|--to <arg> Set ending time. No caps beyond this. Same format |
as -i. |
-T|--title <arg> Add a title above the vidcaps. |
-j|--jpeg Output in jpeg (by default output is in png). |
-j2|--jpeg2 Output in jpeg 2000 |
-V|--dvd DVD Mode. |
In this mode the input <file>s must be the DVD |
device(s) or ISO(s). When in DVD mode all input files |
must be DVDs. |
Implies -A (auto aspect ratio) |
--dvd-title <arg> DVD title to use. Using 0 (the default) will use the |
longest title. |
-M|--mplayer Use Mplayer to capture$mpchosen |
-F|--ffmpeg Use FFmpeg to capture [Default$ffchosen] |
-E|--end-offset <arg> This amount of time is ignored from the end of the |
video. |
Accepts timestamps (same format as -i) and percentages. |
This value is not used when a explicit ending time is |
set. |
The default is $DEFAULT_END_OFFSET. |
-q|--quiet Don't print progress messages just errors. Repeat to |
mute completely, even on error. |
-h|--help Show basic help and exit. |
--fullhelp Show the complete help and exit. |
-d|--disable <arg> Disable some default functionality. |
Features that can be disabled are: |
* timestamps: use -dt or --disable timestamps |
* shadows: use -ds or --disable shadows |
* padding: use -dp or --disable padding |
(note shadows introduce some extra padding) |
-A|--autoaspect Try to guess aspect ratio from resolution. |
-e[num] | --extended=[num] |
Enables extended mode and optionally sets the extended |
factor. -e is the same as -e$DEFAULT_EXT_FACTOR. |
-l|--highlight <arg> Add the frame found at timestamp "arg" as a |
highlight. Same format as -i. |
-m|--manual Manual mode: Only timestamps indicated by the user are |
used (use in conjunction with -S), when using this |
-i and -n are ignored. |
-S|--stamp <arg> Add the frame at timestamp "arg" to the set of captures. |
Same format as -i. |
-u|--user <arg> Set the username (included by default in the sheet's |
footer) to this value. |
-U|--fullname Use user's full/real name (e.g. John Smith) as found |
set in the system's list of users. |
-p|--profile <arg> Load profile "arg" |
-C|--config <arg> Load configuration file "arg" |
--generate <config|profile> |
Generate configuration or profile from current settings |
-k <arg> |
--funky <arg> Funky modes:$funkyex |
$longhelp |
Examples: |
Create a contact sheet with default values (vidcaps at intervals of |
$DEFAULT_INTERVAL seconds), will be saved to 'video.avi.png': |
\$ $P video.avi |
Create a sheet with vidcaps at intervals of 3 and a half minutes, save to |
'output.jpg': |
\$ $P -i 3m30 input.wmv -o output.jpg |
Create a sheet with vidcaps starting at 3 mins and ending at 18 mins, |
add an extra vidcap at 2m and another one at 19m: |
\$ $P -f 3m -t 18m -S2m -S 19m input.avi |
See more examples at vcs' homepage <http://p.outlyer.net/vcs/>. |
EOF |
# ' # Syntax highlighting bait |
} |
# Print a configuration file generated from the currently active settings |
# generate_config($1 = <config|profile>) |
generate_config() { |
local n=$(echo $1 | tr a-z A-Z) f= t= x= |
cat <<-EOM |
# --- $n STARTS HERE --- |
# This is a sample configuration file for VCS generated automatically |
# from the command-line with the "--generate $1" command-line option |
# Save it to ~/.vcs.conf or ~/.vcs/vcs.conf to make it the default |
# configuration. |
# OR |
# Save it to ~/.vcs/profiles/something.conf to create a profile named |
# "something". To use this profile run vcs with the "--profile something" |
# (or "-p something") option |
# OR |
# Save it to "something.conf" and load it with "--config something.conf" |
# (or "-C something.conf") |
EOM |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( -z $t ) || ( $t == '=' ) ]]; then t=$f ; fi |
eval v=\$USR_$t |
[[ -z $v ]] || { |
# Symbolic values: |
case $( tolower "$t" ) in |
timecode_from) |
x='$TC_NUMCAPS' |
[[ $v -eq $TC_NUMCAPS ]] || x='$TC_INTERVAL' |
v=$x |
;; |
decoder) |
x='$DEC_FFMPEG' |
[[ $v -eq $DEC_FFMPEG ]] || x='$DEC_MPLAYER' |
v=$x |
;; |
verbosity) |
case $v in |
$V_ALL) v='$V_ALL' ;; |
$V_NONE) v='$V_NONE' ;; |
$V_INFO) v='$V_INFO' ;; |
$V_WARN) v='$V_WARN' ;; |
$V_ERROR) v='$V_ERROR' ;; |
esac # verbosity |
;; |
esac |
[[ -z $v ]] || { |
# Don't print unnecessary decimals |
if [[ $v =~ ^[0-9][0-9]*\.[0-9][0-9]*$ ]]; then |
v=$(sed -e 's/0*$//' -e 's/\.$//' <<<"$v") |
fi |
} |
# Print all names in lowercase |
echo "$(tolower "$f")=$v" |
} |
done |
echo "# vcs:conf:$NL# Generated on $(date)$NL# --- $n ENDS HERE --- " |
exit 0 |
} |
# }}} # Help / Info |
#### Entry point #### |
# Important to do this before any message can be thrown |
init_feedback |
# Ensure $GETOPT is GNU/Linux-style getopt |
choose_getopt |
# Execute exithdlr on exit |
trap exithdlr EXIT |
show_vcs_info |
# Test requirements. Important, must check before looking at the |
# command line (since getopt is used for the task) |
test_programs |
# The command-line overrides any configuration. And the configuration |
# is able to change the program in charge of parsing options ($GETOPT) |
load_config |
# {{{ # Command line parsing |
# TODO: Find how to do this correctly (this way the quoting of $@ gets messed): |
#eval set -- "${default_options} ${@}" |
ARGS="$@" |
# [[R0]] |
# TODO: Why does FreeBSD's GNU getopt ignore -n?? |
TEMP=$("$GETOPT" -n "$0" -s bash \ |
-o i:n:u:T:f:t:S:j::hFMH:c:ma:l:De::U::qAO:I:k:W:E:d:VR:Z:o:p:C: \ |
--long "interval:,numcaps:,username:,title:,from:,to:,stamp:,jpeg::,help,"\ |
"mplayer,ffmpeg,height:,columns:,manual,aspect:,highlight:"\ |
"extended::,fullname,anonymous,quiet,autoaspect,override:,mincho,funky:,"\ |
"end_offset:,end-offset:,disable:,dvd,dvd-title:,randomsource:,undocumented:,output:,"\ |
"fullhelp,profile:,"\ |
"jpeg2,nonlatin,generate:,config:" \ |
-- "$@") |
eval set -- "$TEMP" |
while true ; do |
case $1 in |
-i|--interval) |
check_constraint 'interval' "$2" "$1" || die |
INTERVAL=$(get_interval $2) |
TIMECODE_FROM=$TC_INTERVAL |
USR_INTERVAL=$INTERVAL |
USR_TIMECODE_FROM=$TC_INTERVAL |
shift # Option arg |
;; |
-n|--numcaps) |
check_constraint 'numcaps' "$2" "$1" || die |
NUMCAPS=$2 |
TIMECODE_FROM=$TC_NUMCAPS |
USR_NUMCAPS=$2 |
USR_TIMECODE_FROM=$TC_NUMCAPS |
shift # Option arg |
;; |
-o|--output) |
current=${#OUTPUT_FILES[@]} |
OUTPUT_FILES[$current]=$2 |
shift ;; |
-u|--username) USERNAME=$2 ; USR_USERNAME=$USERNAME ; shift ;; |
-U|--fullname) |
# -U accepts an optional argument, 0, to make an anonymous signature |
# --fullname accepts no argument |
if [[ $1 == '-U' ]]; then # -U always provides an argument |
if [[ -n $2 ]]; then # With argument, special handling |
if [[ $2 != '0' ]]; then |
error "Use '-U0' to make an anonymous contact sheet or '-u \"My Name\"'" |
error " to sign as My Name. Got -U$2" |
exit $EX_USAGE |
fi |
ANONYMOUS_MODE=1 |
USR_ANONYMOUS_MODE=1 |
fi |
shift |
else # No argument, default handling (try to guess real name) |
idname=$(id -un) |
if type -p getent >/dev/null ; then |
USERNAME=$(getent passwd "$idname" | cut -d':' -f5 | sed 's/,.*//g') |
else |
USERNAME=$(grep "^$idname:" /etc/passwd | cut -d':' -f5 | sed 's/,.*//g') |
fi |
if [[ -z $user ]]; then |
USERNAME=$idname |
error "No fullname found, falling back to default ($USERNAME)" |
fi |
unset idname |
fi |
;; |
--anonymous) ANONYMOUS_MODE=1 ; USR_ANONYMOUS_MODE=1 ;; # Same as -U0 |
-T|--title) TITLE="$2" ; USR_TITLE="$2" ; shift ;; |
-f|--from) |
if ! FROMTIME=$(get_interval "$2") ; then |
error "Starting timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
USR_FROMTIME="$FROMTIME" |
shift |
;; |
-E|--end_offset|--end-offset) |
if [[ $1 == '--end_offset' ]]; then |
warn "Option --end_offset is deprecated and will be removed in the" |
warn " next version, please use --end-offset instead" |
fi |
check_constraint 'end_offset' "$2" "$1" || die |
is_p='y' |
is_percentage "$2" || is_p='' |
if [[ $is_p ]]; then |
END_OFFSET="$2" |
else |
END_OFFSET=$(get_interval "$2") |
fi |
USR_END_OFFSET="$END_OFFSET" |
unset is_i |
shift |
;; |
-t|--to) |
if ! TOTIME=$(get_interval "$2") ; then |
error "Ending timestamp must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
if fptest "$TOTIME" -eq 0 ; then |
error "Ending timestamp was set to 0, set to movie length." |
totime=-1 |
fi |
USR_TOTIME=$TOTIME |
shift |
;; |
-S|--stamp) |
if ! temp=$(get_interval "$2") ; then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
INITIAL_STAMPS=( "${INITIAL_STAMPS[@]}" "$temp" ) |
shift |
;; |
-l|--highlight) |
if ! temp=$(get_interval "$2"); then |
error "Timestamps must be a valid timecode. Got '$2'." |
exit $EX_USAGE |
fi |
HLTIMECODES=( "${HLTIMECODES[@]}" "$temp" ) |
shift |
;; |
--jpeg2) # Note --jpeg 2 is also accepted |
FORMAT=jp2 |
USR_FORMAT=jp2 |
;; |
-j|--jpeg) |
if [[ $2 ]]; then # Arg is optional, 2 is for JPEG 2000 |
# 2000 is also accepted |
if [[ $2 != '2' && $2 != '2000' ]]; then |
error "Use -j for jpeg output or -j2 for JPEG 2000 output. Got '-j$2'." |
exit $EX_USAGE |
fi |
FORMAT=jp2 |
else |
FORMAT=jpg |
fi |
USR_FORMAT="$FORMAT" |
shift |
;; |
-h|--help) show_help ; exit $EX_OK ;; |
--fullhelp) show_help 'full' ; exit $EX_OK ;; |
-F|--ffmpeg) set_capturer ffmpeg ;; |
-M|--mplayer) set_capturer mplayer ;; |
-H|--height) |
check_constraint 'height' "$2" "$1" || die |
HEIGHT="$2" |
USR_HEIGHT="$2" |
shift |
;; |
-a|--aspect) |
if ! is_float "$2" && ! is_fraction "$2" ; then |
error "Aspect ratio must be expressed as a (positive) floating " |
error " point number or a fraction (ie: 1, 1.33, 4/3, 2.5). Got '$2'." |
exit $EX_USAGE |
fi |
ASPECT_RATIO="$2" |
USR_ASPECT_RATIO="$2" |
shift |
;; |
-A|--autoaspect) ASPECT_RATIO=-1 ; USR_ASPECT_RATIO=-1 ;; |
-c|--columns) |
check_constraint 'columns' "$2" "$1" || die |
COLUMNS="$2" |
USR_COLUMNS="$2" |
shift |
;; |
-m|--manual) MANUAL_MODE=1 ;; |
-e|--extended) |
# Optional argument quirks: $2 is always present, set to '' if unused |
# from the commandline it MUST be directly after the -e (-e2 not -e 2) |
# the long format is --extended=VAL |
if [[ $2 ]]; then |
check_constraint 'extended_factor' "$2" "$1" || die |
EXTENDED_FACTOR="$2" |
else |
EXTENDED_FACTOR=$DEFAULT_EXT_FACTOR |
fi |
USR_EXTENDED_FACTOR=$EXTENDED_FACTOR |
shift |
;; |
# Unlike -I, --nonlatin does not accept a font name |
--nonlatin) |
if [[ -z $USR_NONLATIN_FONT ]]; then |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
;; |
-I) |
# Extended/non-latin font |
# New syntax introduced in 1.11: |
# -Ij: Try to pick automatically a CJK font. Might fail and abort |
# -Ij='Font name or file': Set font manually |
# |
# If an argument is passed, test it is one of the known ones |
case $2 in |
k|j|k=*|j=*) ;; |
*) error "-I must be followed by j or k!" && exit $EX_USAGE ;; |
esac |
# It isn't tested for existence because it could also be a font |
# which convert would understand without giving the full path |
NONLATIN_FILENAMES=1 |
USR_NONLATIN_FILENAMES=1 |
if [[ ${#2} -gt 1 ]]; then |
# j=, k= syntax |
NONLATIN_FONT="${2:2}" |
USR_NONLATIN_FONT="$NONLATIN_FONT" |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
# If the user didn't pick one, try to select automatically |
if [[ -z $USR_NONLATIN_FONT ]]; then |
set_extended_font |
inf "Filename font set to '$NONLATIN_FONT'" |
fi |
shift |
;; |
-O|--override) |
# Rough test |
RE='[a-zA-Z_]+=[^;]*' |
if [[ ! $2 =~ $RE ]]; then |
error "Wrong override format, it should be variable=value. Got '$2'." |
exit $EX_USAGE |
fi |
two=$(tolower "$2") |
RE='^[[:space:]]*getopt=' |
if [[ $two =~ $RE ]] ; then # getopt= |
# If we're here, getopt has already been found and works, so it makes no |
# sense to override it; on the other hand, if it hasn't been correctly |
# set/detected we won't reach here |
warn "Setting 'getopt' can't be overridden from the command line." |
else |
cmdline_override "$2" |
POST_GETOPT_HOOKS+=( 1:cmdline_overrides_flush ) |
fi |
shift |
;; |
-W) |
case $2 in |
# (classic) Workaround mode. See wa_ss_* declarations at the start for details |
o) wa_ss_af='-ss ' ; wa_ss_be='' ;; |
# Console colout |
# Once: Disable console colour, use prefixes instead |
# Twice: Disable prefixes too |
c) |
set_feedback_prefixes |
[[ -n $UNDFLAG_NOPREFIX ]] && SIMPLE_FEEDBACK=1 |
UNDFLAG_NOPREFIX=1 |
;; |
# Double length of video probed in safe measuring |
# Semi-undocumented traits: |
# - Can be repeated, will double for each instance |
# - -Ws -Ws -Ws = -Ws3 |
s|s[0-9]|s[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_MAX_REWIND=$(awkexf "$QUIRKS_MAX_REWIND * (2^$n)") |
(( INTERNAL_WS_C+=n ,1 )) |
;; |
# Brute force -Ws: Test all the length of the file if required |
S) QUIRKS_MAX_REWIND=-1 ;; |
# Increase precission of safe length measuring (halve the stepping) |
# Like -Ws can be repeated |
p|p[0-9]|p[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP / (2^$n)") |
(( INTERNAL_WP_C+=n ,1 )) |
;; |
# Inverse of -Wp: Decrease precission of safe length measuring |
# i.e.: will try less times <-> will be quicker but less accurate |
# desirable when -Ws or -WS are used. |
# Can also be repeated |
P|P[0-9]|P[0-9][0-9]) |
[[ ${#2} -gt 1 ]] && n=${2:1} || n=1 |
QUIRKS_LEN_STEP=$(awkexf "$QUIRKS_LEN_STEP * (2^$n)") |
(( INTERNAL_WP_C-=n ,1 )) |
;; |
# -Wb (Semi-undocumented): Disable safe mode. Use this to force accepting |
#+broken/partial files. Only makes sense when testing or in combination |
#+with stuff like '-Z idonly' |
b) QUIRKS=-2 ;; # Quirks < 0 : No safe mode |
*) |
error "Wrong argument. Use --fullhelp for a list available workarounds. Got -W$2." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-k|--funky) # Funky modes |
case "$2" in # Note older versions (<1.0.99) were case-insensitive |
p|polaroid) # Same as overlap + rotate + polaroid |
inf "Polaroid mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# XXX: The newer version has a lot less flexibility with these many |
# hardcoded values... |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
c|photos) # Same as overlap + rotate + photoframe, this is the older polaroid |
inf "Photos mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' 'filt_randrot' ) |
CSHEET_DELEGATE='csheet_overlap' |
# The timestamp must change location to be visible most of the time |
GRAV_TIMESTAMP=NorthWest |
;; |
o|overlap) # Random overlap mode |
inf "Overlap mode enabled." |
CSHEET_DELEGATE='csheet_overlap' |
GRAV_TIMESTAMP=NorthWest |
;; |
r|rotate) # Random rotation |
inf "Random rotation of captures enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_randrot' ) |
;; |
f|photoframe) # White photo frame |
inf "Photoframe mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_photoframe' ) |
;; |
L|polaroidframe) # White polaroid frame |
inf "Polaroid frame mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_polaroid ') |
GRAV_TIMESTAMP=South |
FG_TSTAMPS=Black |
BG_TSTAMPS=Transparent |
PTS_TSTAMPS=$(( $PTS_TSTAMPS * 3 / 2 )) |
;; |
i|film) |
inf "Film mode enabled." |
FILTERS_IND=( "${FILTERS_IND[@]}" 'filt_film' ) |
;; |
x|random) # Random colours/fonts |
inf "Fonts and colours randomisation enabled." |
randomize_look |
;; |
*) |
error "Unknown funky mode requested. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-p|--profile) |
case $2 in |
classic) # Classic colour scheme |
BG_HEADING=YellowGreen BG_SIGN=SlateGray BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
1.0) # 1.0a, 1.0.1a and 1.0.2b colourscheme |
BG_HEADING=YellowGreen BG_SIGN=SandyBrown BG_CONTACT=White |
BG_TITLE=White FG_HEADING=Black FG_SIGN=Black |
;; |
*) load_profile "$2" || die |
;; |
esac |
shift |
;; |
-C|--config) |
if [[ $2 =~ ^: ]]; then |
if [[ $2 == ':pwd' ]]; then |
cfg=./vcs.conf |
else |
error "Configuration names starting with ':' are reserved." |
exit $EX_USAGE |
fi |
else |
cfg=$2 |
fi |
[[ -f $cfg ]] || { |
error "Configuration file '$cfg' not found" |
exit $EX_USAGE |
} |
# ./vcs.conf doesn't need the vcs:conf: mark |
if [[ $2 != ':pwd' ]]; then |
head -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || \ |
tail -5 "$cfg" | grep -q '#[[:space:]]*vcs:conf[[:space:]]*:' || { |
error "No vcs:conf: mark found in '$cfg'" |
exit $EX_NOINPUT |
} |
fi |
load_config_file "$cfg" 'Custom configuration' |
shift |
;; |
-R|--randomsource) |
if [[ ! -r $2 ]]; then |
error "Random source file '$2' can't be read" |
exit $EX_USAGE |
fi |
init_filerand "$2" |
inf "Using '$2' as source of semi-random values" |
RANDFUNCTION=filerand |
shift |
;; |
-d|--disable) # Disable default features |
case $(tolower "$2") in |
# timestamp (with no final s) is undocumented but will stay |
t|timestamps|timestamp) |
if [[ $DISABLE_TIMESTAMPS -eq 0 ]]; then |
inf "Timestamps disabled." |
# They'll be removed from the filter chain in coherence_check |
DISABLE_TIMESTAMPS=1 |
fi |
;; |
s|shadows|shadow) |
if [[ $DISABLE_SHADOWS -eq 0 ]]; then |
inf "Shadows disabled." |
# They will be removed from the filter chain in coherence_check |
DISABLE_SHADOWS=1 |
fi |
;; |
p|padding) |
if [[ $PADDING -ne 0 ]] ; then |
inf "Padding disabled." # Kinda... |
PADDING=0 |
fi |
;; |
*) |
error "Requested disabling unknown feature. Got '$2'." |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
--dvd-title) |
check_constraint 'dvd_title' "$2" "$1" || die |
DVD_TITLES=( "${DVD_TITLES[@]}" "$2" ) |
shift |
;; |
-V|--dvd) |
# XXX; Are there systems with no perl??? |
if ! type -pf perl >/dev/null ; then |
error "DVD support requires perl" |
exit $EX_UNAVAILABLE |
fi |
# DVD Mode requires lsdvd |
if ! type -pf lsdvd >/dev/null ; then |
error "DVD support requires the lsdvd program" |
exit $EX_UNAVAILABLE |
fi |
DVD_MODE=1 |
ASPECT_RATIO=-2 # Special value: Auto detect only if ffmpeg couldn't |
;; |
-q|--quiet) |
# -q to only show errors |
# -qq to be completely quiet |
if [[ $VERBOSITY -gt $V_ERROR ]]; then |
VERBOSITY=$V_ERROR |
else |
VERBOSITY=$V_NONE |
fi |
USR_VERBOSITY=$VERBOSITY |
;; |
-Z|--undocumented) |
# This is a container for, of course, undocumented functions |
# These are used for testing/debugging purposes. Might (and will) |
# change between versions, break easily and do no safety checks. |
# In short, don't look at them unless told to do so :P |
case "$2" in |
# AWK was used for a little while in a WiP version |
#set_awk=*) AWK="$(cut -d'=' -f2<<<"$2")" ; warn "[U] AWK=$AWK" ;; |
# Hang the main process loop just before cleanup. |
hang) UNDFLAG_HANG="On" ; warn "[U] Hang flag" ;; |
# Print identification results, do nothing else |
idonly) UNDFLAG_IDONLY="On" ; warn "[U] Id only" ;; |
# ffmpeg path |
set_ffmpeg=*) |
FFMPEG_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $FFMPEG_BIN ]]' |
warn "[U] FFMPEG_BIN=$FFMPEG_BIN" |
;; |
# mplayer path |
set_mplayer=*) |
MPLAYER_BIN=$(realpathr "$(cut -d'=' -f2<<<"$2")") |
assert '[[ -x $MPLAYER_BIN ]]' |
warn "[U] MPLAYER_BIN=$MPLAYER_BIN" |
;; |
# Ignore one of the players |
disable_ffmpeg) |
FFMPEG_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/ffmpeg//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "FFmpeg disabled" |
assert '[[ $MPLAYER_BIN ]]' |
set_capturer mplayer |
;; |
disable_mplayer) |
MPLAYER_BIN='' |
CAPTURERS_AVAIL=( $(sed 's/mplayer//'<<<"${CAPTURERS_AVAIL[*]}") ) |
warn "Mplayer disabled" |
assert '[[ $FFMPEG_BIN ]]' |
set_capturer ffmpeg |
;; |
debug) |
warn "[U] debug" |
DEBUG=1 |
;; |
trace=*) # (Implies 'debug'), traces a particular function name |
INTERNAL_TRACE_FILTER=$(cut -d'=' -f2 <<<"$2") |
DEBUG=1 |
warn "[U] debug, tracing '$INTERNAL_TRACE_FILTER'" |
;; |
# Dump user-set variables and exit [since 1.12] |
uservars) |
echo "${OVERRIDE_MAP[*]}" | stonl | egrep -v '(deprecated=|alias)' | cut -d':' -f1-2 |\ |
while read ovname ; do |
f=${ovname/:*} |
t=${ovname#*:} |
if [[ ( $t ) && ( $t != '=' ) ]]; then f="$t" ; fi |
eval v=\$USR_$f |
[[ -z $v ]] || echo "$(tolower $f)=$v" |
done |
exit 0 |
;; |
functest) # Test a function: -Z functest <funcname> <arg> [arg] [...] |
shift 3 # We're quitting anyway |
funcname=$1 |
shift |
if [[ $(type -t "$funcname") != 'function' ]]; then |
error "functest can only test actual functions" |
exit $EX_USAGE |
fi |
inf "Testing $funcname($*)" |
$funcname "$@" |
exit 0 |
;; |
display) UNDFLAG_DISPLAY=1 ;; |
discard) UNDFLAG_DISCARD=1 ;; |
*) |
error "Unknown \`--undocumented $2' option" |
;; |
esac |
shift |
;; |
--generate) |
case "$2" in |
profile|config) |
POST_GETOPT_HOOKS=( "${POST_GETOPT_HOOKS[@]}" \ |
10:generate_config:$2 ) |
;; |
*) |
error "Option --generate must be followed by profile or config" |
exit $EX_USAGE |
;; |
esac |
shift |
;; |
-D) # Repeat to just test consistency |
if [[ $DEBUGGED -gt 0 ]]; then |
pick_tools # Simulate a normal run |
infplain '[ svn $Rev$ ]' |
# Even when empty, POSIXLY_CORRECT has an effect, check if it's |
# set ([[BIS]]) |
if [[ -n ${POSIXLY_CORRECT+x} ]]; then |
pc="'${POSIXLY_CORRECT}'" |
else |
pc='{not set}' |
fi |
# AWK and sed version can't be checked in all variants |
awkv=$(awk --version 2>/dev/null | head -1) || true |
if [[ -n $awkv ]]; then |
awkv="${NL}AWK: $awkv" |
fi |
sedv=$(sed --version 2>/dev/null | head -1) || true |
if [[ -n $sedv ]]; then |
sedv="${NL}sed: $sedv" |
fi |
usrcap= |
if [[ -n $USR_CAPTURER ]]; then |
usrcap=$USR_CAPTURER |
else |
usrcap='{default}' |
fi |
evasion="Enabled (${EVASION_ALTERNATIVES[*]})" |
if [[ $DISABLE_EVASION -eq 1 ]]; then |
evasion='Disabled' |
fi |
cat >&2 <<-EOD |
=== Setup === |
GETOPT: $GETOPT |
MPLAYER: $MPLAYER_BIN |
FFMPEG: $FFMPEG_BIN |
AWK: $(realpathr $(type -pf awk)) |
sed: $(realpathr $(type -pf sed)) |
POSIXLY_CORRECT: $pc |
Capturers (av.): [ ${CAPTURERS_AVAIL[*]} ] |
Identif. (av.): [ ${IDENTIFIERS_AVAIL[*]} ] |
Capturer: $CAPTURER |
Chosen capturer: $usrcap |
Filterchain: [ ${FILTERS_IND[*]} ] |
Safe step: $QUIRKS_LEN_STEP |
Blank evasion: $evasion |
=== Versions === |
Bash: $BASH_VERSION |
Getopt: $($GETOPT --version)$awkv$sedv |
EOD |
exit |
fi |
DEBUG=1 |
VERBOSITY=$V_ALL |
inf "Testing internal consistency..." |
tmp=$INTERNAL_NO_TRACE |
INTERNAL_NO_TRACE=1 # Avoid any tracing during the test |
internal_integrity_test && warn "All tests passed" || error "Some tests failed!" |
INTERNAL_NO_TRACE=$tmp |
unset tmp |
DEBUGGED=1 |
warn "Command line: $0 $ARGS" |
TITLE="$(basename "$0") $ARGS" |
;; |
--) shift ; break ;; |
*) error "Internal error! (remaining opts: $*)" ; exit $EX_SOFTWARE ; |
esac |
shift |
done |
# Avoid coherence_check if there's no arguments and no cmdline post |
# processing |
[[ -n $1 || -n $POST_GETOPT_HOOKS ]] || { |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
} |
# More than one argument... |
if [[ -n $2 ]]; then |
multiple_input_files=1 |
fi |
# }}} # Command line parsing |
# The coherence check ensures the processed options are |
# not incoherent/incompatible with the input files or with |
# other given options |
coherence_check || { |
exit $? |
} |
# Run after coherence check to clean recoverable incorrect values |
post_getopt_hooks |
pick_tools |
# Remaining arguments |
if [[ -z $1 ]]; then |
[[ $VERBOSITY -eq $V_NONE ]] || show_help |
exit $EX_USAGE |
fi |
# TODO: |
# DVD mode + multiple titles is still tricky: |
# --dvd --dvd-title 1 --dvd-title 2 /dev/dvd /dev/dvd |
set +e # Don't fail automatically. Blocks marked with {{SET_E}} will break if this changes |
for arg do process "$arg" ; done |
# Script ends here, everything below are comments |
# =========================================================================== |
# |
# Bash syntax notes # {{{ |
# These are some notes for my own reference (or for those trying to read the script) |
# regarding bash syntax nuissances. |
# |
# * see http://www.gnu.org/s/bash/manual/html_node/Bash-Variables.html for builtin vars |
# * herestring redirection, '<<<$string', (used extensively in vcs) was introduced in bash 2.05b |
# * sed s/[ ,]/ * /g <=> ${var//[ ,]/ * } [Much faster due to not forking] |
# sed s/[ ,]/ * / <=> ${var/[ ,]/ * } |
# * bash2: declaring local empty arrays like 'local a=( )' makes bash think they're strings |
# 'local -a' must be used instead |
# bash3 has no problem with this |
# * bash2: 'arr+=( elem )' for array push is not supported, use 'arr=( "${arr[@]}" elem )' instead |
# += is a bash3 syntax modification, bash3.1 extended it further, arithmetic += works |
# inside let |
# * bash2: [*] expands as a string while [@] expands as an array. Both have trouble with spaces |
# in elements though |
# * bash3: [[ STR =~ EREGEX ]] is faster than grep/egrep (no forking) |
# bash 3.2 changed semantics vs bash 3.1 |
# quoting the ERE poses a problem (newer bash will interpret as plain string, older |
# as ERE), storing the ERE in a variable or writing it unquoted solves this problem |
# * bash4: |& (inherited from csh?) pipes both stdout and stderr |
# * [[ A == $B ]] : $B should be quoted usually, otherwise it will be scanned as a regex |
# * performance: bash loops are often slower than awk or perl |
# * performance: grep + cut proved faster than an equivalent sed -r s// replacement |
# }}} # Bash syntax notes |
# |
# vim:set ts=4 ai foldmethod=marker nu: # |
Property changes: |
Added: svn:executable |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/changelog |
---|
0,0 → 1,101 |
vcs (1.13.2-pon.1) UNRELEASED; urgency=medium |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 16 May 2014 17:09:00 +0200 |
vcs (1.13.1-pon.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Wed, 26 Feb 2014 01:41:27 +0100 |
vcs (1.13-pon.1) experimental; urgency=low |
* New version. |
* debian/changelog: Changed to shorter suffix |
-- Toni Corvera <outlyer@gmail.com> Wed, 27 Feb 2013 16:57:12 +0100 |
vcs (1.12.3-upstream.1) experimental; urgency=low |
* New version. |
* debian/control: Bump minimum bash version |
-- Toni Corvera <outlyer@gmail.com> Sun, 17 Jul 2011 18:49:56 +0200 |
vcs (1.12.2-upstream.1) experimental; urgency=medium |
* New version. Medium priority due to temporary files cleanup bug. |
-- Toni Corvera <outlyer@gmail.com> Tue, 24 Aug 2010 20:48:41 +0200 |
vcs (1.12.1-upstream.1) experimental; urgency=medium |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 23 Apr 2010 13:56:58 +0200 |
vcs (1.12-upstream.1) experimental; urgency=low |
* New version. |
* debian/docs: Install vcs.conf.example |
-- Toni Corvera <outlyer@gmail.com> Sat, 10 Apr 2010 00:57:17 +0200 |
vcs (1.11.2-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Fri, 19 Mar 2010 00:18:51 +0100 |
vcs (1.11.1-upstream.1) experimental; urgency=low |
* New version. |
-- Toni Corvera <outlyer@gmail.com> Tue, 11 Mar 2010 00:07:28 +0100 |
vcs (1.11-upstream.1) experimental; urgency=low |
* debian/control: |
- Added min. bash version |
- Rw-worded short description |
- Don't Depend on bc anymore |
- Remove mktemp (now coreutils) from Depends:, they're essential anyway |
- Bumped min IM to 6.3.5-7 |
-- Toni Corvera <outlyer@gmail.com> Sun, 07 Mar 2010 21:47:41 +0100 |
vcs (1.0.100a-upstream.1) experimental; urgency=low |
* New version |
-- Toni Corvera <outlyer@gmail.com> Fri, 10 Apr 2009 17:08:33 +0200 |
vcs (1.0.99-upstream.0) experimental; urgency=low |
* New version. |
* debian/control: |
- Added lsdvd as recommendation (required for dvd support) |
- Using 'Homepage:' |
-- Toni Corvera <outlyer@gmail.com> Wed, 11 Mar 2009 22:50:25 +0100 |
vcs (1.0.12-upstream.1) experimental; urgency=low |
* debian/control: Added missing requirement (gsfonts) |
-- Toni Corvera <outlyer@gmail.com> Mon, 06 Oct 2008 14:26:27 +0200 |
vcs (1.0.12-upstream.0) experimental; urgency=low |
* New version. |
* Added suffix to version number. |
-- Toni Corvera <outlyer@gmail.com> Wed, 16 Apr 2008 17:59:46 +0200 |
vcs (1.0.11) experimental; urgency=low |
* First package released. |
-- Toni Corvera <outlyer@gmail.com> Tue, 08 Apr 2008 21:15:14 +0200 |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/dirs |
---|
0,0 → 1,2 |
usr/bin |
usr/share |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/docs |
---|
0,0 → 1,2 |
examples/ |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/rules |
---|
0,0 → 1,98 |
#!/usr/bin/make -f |
# -*- makefile -*- |
# Sample debian/rules that uses debhelper. |
# This file was originally written by Joey Hess and Craig Small. |
# As a special exception, when this file is copied by dh-make into a |
# dh-make output file, you may use that output file without restriction. |
# This special exception was added by Craig Small in version 0.37 of dh-make. |
# Uncomment this to turn on verbose mode. |
#export DH_VERBOSE=1 |
DESTDIR:=$(CURDIR)/debian/vcs |
CFLAGS = -Wall -g |
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) |
CFLAGS += -O0 |
else |
CFLAGS += -O2 |
endif |
configure: configure-stamp |
configure-stamp: |
dh_testdir |
# Add here commands to configure the package. |
touch configure-stamp |
build: build-stamp |
build-stamp: configure-stamp |
dh_testdir |
# Add here commands to compile the package. |
$(MAKE) all prepackage |
#docbook-to-man debian/vcs.sgml > vcs.1 |
touch $@ |
clean: |
dh_testdir |
dh_testroot |
rm -f build-stamp configure-stamp |
# Add here commands to clean up after the build process. |
-$(MAKE) clean |
dh_clean |
install: build |
dh_testdir |
dh_testroot |
dh_clean -k |
dh_installdirs |
# Add here commands to install the package into debian/vcs. |
$(MAKE) DESTDIR=$(DESTDIR) prefix=/usr install |
# Build architecture-independent files here. |
binary-indep: build install |
# We have nothing to do by default. |
# Build architecture-dependent files here. |
binary-arch: build install |
dh_testdir |
dh_testroot |
dh_installchangelogs CHANGELOG |
dh_installdocs |
dh_installexamples |
# dh_install |
# dh_installmenu |
# dh_installdebconf |
# dh_installlogrotate |
# dh_installemacsen |
# dh_installpam |
# dh_installmime |
# dh_python |
# dh_installinit |
# dh_installcron |
# dh_installinfo |
dh_installman docs/vcs.1 docs/vcs.conf.5 |
dh_link |
dh_strip |
dh_compress |
dh_fixperms |
# dh_perl |
# dh_makeshlibs |
dh_installdeb |
dh_shlibdeps |
dh_gencontrol |
dh_md5sums |
dh_builddeb |
binary: binary-indep binary-arch |
.PHONY: build clean binary-indep binary-arch binary install configure |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/control |
---|
0,0 → 1,17 |
Source: vcs |
Section: contrib/graphics |
Priority: extra |
Maintainer: Toni Corvera <outlyer@gmail.com> |
Build-Depends: debhelper (>= 5) |
Standards-Version: 3.7.2 |
Homepage: http://p.outlyer.net/vcs/ |
Package: vcs |
Architecture: all |
Depends: bash (>= 3.1), imagemagick (>= 6.3.5-7), mplayer | ffmpeg |
Recommends: lsdvd, ttf-dejavu-core |
Description: tool to create contact sheets (previews) from videos |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/copyright |
---|
0,0 → 1,35 |
This package was debianized by Toni Corvera <outlyer@gmail.com> on |
Mon, 04 Feb 2008 03:32:28 +0100. |
It was downloaded from <http://p.outlyer.net/vcs/> |
Upstream Author: |
Toni Corvera <outlyer@gmail.com> |
Copyright: |
<Copyright (C) 2007 Toni Corvera> |
License: |
This package is free software; you can redistribute it and/or |
modify it under the terms of the GNU Lesser General Public |
License as published by the Free Software Foundation; either |
version 2 of the License, or (at your option) any later version. |
This package is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
Lesser General Public License for more details. |
You should have received a copy of the GNU Lesser General Public |
License along with this package; if not, write to the Free Software |
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
On Debian systems, the complete text of the GNU Lesser General |
Public License can be found in `/usr/share/common-licenses/LGPL'. |
The Debian packaging is (C) 2008, Toni Corvera <outlyer@gmail.com> and |
is licensed under the GPL, see `/usr/share/common-licenses/GPL'. |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/debian/compat |
---|
0,0 → 1,0 |
5 |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/arch/PKGBUILD.in |
---|
0,0 → 1,42 |
# |
# $Rev$ |
# |
# Build with '$ makepkg' on the same directory as this file |
# |
# Maintainer: Toni Corvera (Upstream) <outlyer@gmail.com> |
pkgname=vcs |
pkgver=@VERSION@ |
pkgrel=1 |
pkgdesc="tool to create contact sheets (previews) from videos" |
arch=('any') |
url="http://p.outlyer.net/vcs/" |
license=('LGPL') |
depends=('bash>=3.1' 'imagemagick>=6.3.5.7' 'mplayer' 'ffmpeg') |
makedepends=('bzip2') |
optdepends=('lsdvd: for DVD support' |
'perl: for DVD support') |
backup=() |
options=('docs' 'zipman') |
source=($url/files/$pkgname-$pkgver.tar.gz) |
md5sums=(@MD5@) #generate with 'makepkg -g' |
sha1sums=(@SHA1@) |
# Debian & Arch didn't agree on this on my first try (???) |
sha256sums=(@SHA256@) |
prepare() { |
cd $srcdir/$pkgname-$pkgver |
make prepackage |
} |
package() { |
cd $srcdir/$pkgname-$pkgver |
make install DESTDIR=${pkgdir} prefix=/usr |
install -D $srcdir/$pkgname-$pkgver/examples/vcs.conf.example \ |
${pkgdir}/usr/share/doc/$pkgname/vcs.conf.example |
} |
#man page (TODO) |
# install -D -m644 $pkgname.1 ${pkgdir}/usr/share/man1/$pkgname.1 || return 1 |
# vim:set filetype=sh ts=2 et: # |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/rpm/vcs.spec.in |
---|
0,0 → 1,121 |
# |
# $Rev$ |
# |
# spec file for vcs rpm |
# |
# based on mp3plot's which in turn was based on other sources |
# |
%define is_mandrake %(test -e /etc/mandrake-release && echo 1 || echo 0) |
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0) |
%define is_fedora 0%{?fedora} |
%define is_redhat 0%{?rhl} |
%define is_rhel 0%{?rhel} |
%define distname generic |
%define disttag .generic |
%if %{is_fedora} |
%define distname fedora |
%define disttag %{dist} |
%endif |
%if %{is_redhat} |
%define distname redhat |
%define disttag %{dist} |
%endif |
%if %{is_mandrake} |
%define distname mandrake |
%define disttag .mdk |
%endif |
%if %{is_suse} |
%define distname suse |
%define disttag .suse |
%endif |
%if %{is_rhel} |
%define distname rhel |
%define disttag %{dist} |
%endif |
Name: vcs |
Summary: Tool to create contact sheets (previews) from videos |
Version: @VERSION@ |
Release: pon1%{?disttag} |
License: LGPL |
Packager: @PACKAGER@ |
Group: Applications/Multimedia |
Source0: http://p.outlyer.net/%{name}/files/%{name}-%{version}.tar.gz |
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build |
BuildArch: noarch |
# TODO: How to set alternative dependencies? i.e. mplayer/ffmpeg |
Requires: mplayer / ffmpeg |
Requires: bash >= 3.1 |
Requires: ImageMagick >= 6.3.5-7 |
Requires: coreutils |
URL: http://p.outlyer.net/vcs/ |
#BuildRequires: |
#Prereq: /sbin/ldconfig |
#Requires: |
AutoReqProv: yes |
## Allow relocation (e.g. rpm --prefix /opt/vcs) |
Prefix: /usr |
%description |
Video Contact Sheet *NIX (vcs for short) is a script that creates a contact |
sheet (preview) from videos by taking still captures distributed over the |
length of the video. The output image contains useful information on the video |
such as codecs, file size, screen size, frame rate, and length. |
%prep |
#echo %_target |
echo Building %{name}-%{version}-%{release} |
%setup -q -n %{name}-%{version} |
%build |
make examples/vcs.conf.example |
%install |
make DESTDIR=%buildroot prefix=%{prefix} install |
%clean |
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT} |
#%post |
# postinst |
#%postun |
# postuninst |
%files |
%defattr(-,root,root) |
# binary |
%{_bindir}/%{name} |
# Profiles |
%{prefix}/share/vcs/profiles/black.conf |
%{prefix}/share/vcs/profiles/mosaic.conf |
%{prefix}/share/vcs/profiles/white.conf |
%{prefix}/share/vcs/profiles/compact.conf |
# Manpages |
%{_mandir}/man1/%{name}.1.gz |
%{_mandir}/man5/%{name}.conf.5.gz |
%doc CHANGELOG |
# Config example |
%doc examples/vcs.conf.example |
%changelog |
* Fri Mar 08 2013 - outlyer (at) gmail (dot) com |
- Install 'compact' profile |
* Sun Aug 28 2011 - outlyer (at) gmail (dot) com |
- Install additional manpage for configuration file |
* Tue Aug 24 2010 - outlyer (at) gmail (dot) com |
- Install manpage |
* Sat Apr 10 2010 - outlyer (at) gmail (dot) com |
- Added profiles and example configuration |
- Use %{prefix} |
* Sun Mar 07 2010 - outlyer (at) gmail (dot) com |
- Initial RPM packaging |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/BSDmakefile |
---|
0,0 → 1,16 |
# |
# $Id$ |
# Makefile for BSD-make |
# |
VERSION!=sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1 |
PACKAGER!=finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3 |
.if empty($(PACKAGER)) |
PACKAGER!=getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1 |
.endif |
GMAKE?=gmake |
RM?=rm -f |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/GNUmakefile |
---|
0,0 → 1,15 |
# |
# $Id$ |
# Makefile for GNU-make |
# |
VERSION:=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' vcs | head -n1) |
PACKAGER:=$(shell finger -lp `echo $USER` 2>/dev/null | head -n1 | cut -d: -f3) |
ifeq ($(PACKAGER),) |
PACKAGER:=$(shell getent passwd "`id -un`" | cut -d: -f5 | cut -d, -f1) |
endif |
GMAKE?=make |
include common.mk |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/docs/src/settings.man.inc.xml |
---|
0,0 → 1,591 |
<!DOCTYPE variablelist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
]> |
<!-- $Date: 2011-09-08 04:58:56 +0200 (dj, 08 set 2011) $ --> |
<variablelist id="settings" lang="en-GB"> |
<varlistentry> |
<term id="term-all">All settings</term> |
<listitem> |
<para> |
<!-- |
$ grep '<term' src/settings.man.inc.xml |\ |
sed -r -e '/<term id="term-all/d' \ |
-e 's/^[[:space:]]*//' \ |
-e 's!<term id="(.*)"><literal>.*$!<xref linkend="\1" />,!' \ |
-e 's/^/ /' \ |
-e '/(shoehorned|safe_rename_pattern)/d' |
--> |
<xref linkend="term-anonymous" />, |
<xref linkend="term-bg_all" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_title" />, |
<xref linkend="term-bg_tstamps" />, |
<xref linkend="term-capturer" />, |
<xref linkend="term-columns" />, |
<xref linkend="term-debug" />, |
<xref linkend="term-decoder" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_shadows" />, |
<xref linkend="term-disable_timestamps" />, |
<xref linkend="term-end_offset" />, |
<xref linkend="term-extended_factor" />, |
<xref linkend="term-fg_all" />, |
<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" />, |
<xref linkend="term-fg_tstamps" />, |
<xref linkend="term-font_all" />, |
<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" />, |
<xref linkend="term-font_tstamps" />, |
<xref linkend="term-format" />, |
<xref linkend="term-getopt" />, |
<xref linkend="term-height" />, |
<xref linkend="term-interval" />, |
<xref linkend="term-nonlatin_filenames" />, |
<xref linkend="term-nonlatin_font" />, |
<xref linkend="term-numcaps" />, |
<xref linkend="term-padding" />, |
<xref linkend="term-plain_messages" />, |
<xref linkend="term-profiles" />, |
<xref linkend="term-pts_meta" />, |
<xref linkend="term-pts_sign" />, |
<xref linkend="term-pts_title" />, |
<xref linkend="term-pts_tstamps" />, |
<xref linkend="term-quality" />, |
<xref linkend="term-signature" />, |
<xref linkend="term-stderr" />, |
<xref linkend="term-stdout" />, |
<xref linkend="term-timecode_from" />, |
<xref linkend="term-user" />, |
<xref linkend="term-verbosity" /> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-anonymous"><literal>anonymous</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Enables or disables the anonymous mode.</para> |
<para>Set to <literal>1</literal> to enable this mode, in which the contact sheet |
footer won't include the |
«Preview created by <link linkend="term-user"><replaceable>$user</replaceable></link>» |
line.</para> |
<para>Default: <literal>0</literal> (≡ disabled).</para> |
<para>Equivalent command-line option: <option>--anonymous</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_all"><literal>bg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>bg_</literal> variables at once |
(<xref linkend="term-bg_contact" />, |
<xref linkend="term-bg_heading" />, |
<xref linkend="term-bg_sign" />, |
<xref linkend="term-bg_tstamps" /> and |
<xref linkend="term-bg_title" />).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-bg_heading"><literal>bg_heading</literal></term> |
<term id="term-bg_contact"><literal>bg_contact</literal></term> |
<term id="term-bg_sign"><literal>bg_sign</literal></term> |
<term id="term-bg_title"><literal>bg_title</literal></term> |
<term id="term-bg_tstamps"><literal>bg_tstamps</literal></term> |
<listitem> |
<para>These variables control the background colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">colour |
names</ulink> or <acronym>HTML</acronym>/<acronym>CSS</acronym>-style colour |
specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>bg_heading</literal> &emdash; File meta information (size, codec, etc.). |
Default: <literal>#afcd7a</literal> |
[≡ <literal>RGB(175,205,122)</literal>]</para> |
<para><literal>bg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_contact</literal> &emdash; Captures. |
Default: <constant>White</constant> |
[≡ <literal>RGB(255,255,255)</literal>]</para> |
<para><literal>bg_tstamps</literal> &emdash; Timestamps boxes. |
Default: <literal>#000000aa</literal> |
[≡ <literal>RGBA(0,0,0,0.67)</literal>]</para> |
<para><literal>bg_sign</literal> &emdash; Footer. |
Default: <constant>SlateGray</constant> |
[≡ <literal>RGB(112,128,144)</literal>]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-capturer"><literal>capturer</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>ffmpeg</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>mplayer</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>ffmpeg</symbol></literal></para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
<para role="aside">Since version 1.13</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-columns"><literal>columns</literal></term> |
<listitem> |
<para>Number of columns</para> |
<para>Default: <literal>2</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-debug"><literal>debug</literal></term> |
<listitem> |
<para>Enable or disable debug mode. Set to <userinput>1</userinput> to enable.</para> |
<para>Default: <literal>0</literal> (disabled).</para> |
<para>Equivalent command-line option: <option>-D</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-decoder"><literal>decoder</literal></term> |
<listitem> |
<warning> |
<para>This setting is <emphasis role="strong">deprecated</emphasis>, use |
<xref linkend="term-capturer" /> instead. Notice <xref linkend="term-capturer" /> |
has a different syntax.</para> |
</warning> |
<para>Controls which capturer to use.</para> |
<para>Symbolic values: <literal><symbol>$DEC_FFMPEG</symbol></literal> ⇒ FFmpeg, |
<literal><symbol>$DEC_MPLAYER</symbol></literal> ⇒ MPlayer</para> |
<para>Default: <literal><symbol>$DEC_FFMPEG</symbol></literal> (FFmpeg) </para> |
<para>Related command-line options: |
<option>-F</option>, <option>--ffmpeg</option> and |
<option>-M</option>, <option>--mplayer</option> |
</para> |
<warning> |
<para>DVD mode sets the capturer to MPlayer disregarding the value of |
this setting.</para> |
</warning> |
</listitem> |
</varlistentry> |
<!-- There is NO such setting, but padding=0 can be used instead |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_padding</literal></term> |
<listitem> |
<para>Disables padding when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dp</option>, <option>-disable padding</option>.</para> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-disable_shadows"><literal>disable_shadows</literal></term> |
<listitem> |
<para>Disables drop shadows when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-ds</option>, <option>--disable shadows</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-disable_timestamps"><literal>disable_timestamps</literal></term> |
<listitem> |
<para>Disables timestamps on captures when set to <literal>1</literal>.</para> |
<para>Default: <literal>0</literal></para> |
<para>Equivalent command-line option: <option>-dt</option>, <option>--disable timestamps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-end_offset"><literal>end_offset</literal></term> |
<listitem> |
<para>End offset value (amount of time ignored from the end of videos).</para> |
<para>Can be a percentage (of the detected length of each video) |
or an amount of time, specified in the time syntax specified in &vcsmanpage;.</para> |
<para>Default: <literal>5%</literal></para> |
<para>Equivalent command-line option: <option>-E</option>, <option>--end-offset</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-extended_factor"><literal>extended_factor</literal></term> |
<listitem> |
<para>Extended factor value.</para> |
<para>When set to a value different than <literal>0</literal> enables extended mode.</para> |
<para>Default: <literal>0</literal></para> |
<para>See the <ulink url="http://p.outlyer.net/dox/vcs:extended_mode">extended mode</ulink> |
documentation.</para> |
<para>Equivalent command-line option: <option>-e</option>, <option>--extended</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_all"><literal>fg_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>fg_</literal> variables at once |
(<xref linkend="term-fg_heading" />, |
<xref linkend="term-fg_sign" />, |
<xref linkend="term-fg_title" /> and |
<xref linkend="term-fg_tstamps" />).</para> |
<para role="aside">Since version 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-fg_heading"><literal>fg_heading</literal></term> |
<term id="term-fg_sign"><literal>fg_sign</literal></term> |
<term id="term-fg_title"><literal>fg_title</literal></term> |
<term id="term-fg_tstamps"><literal>fg_tstamps</literal></term> |
<listitem> |
<para>These variables control the font colours of each section in the contact sheet.</para> |
<note> |
<para>Valid colour values are those understood by <application>ImageMagick</application>, |
e.g. <ulink url="http://www.imagemagick.org/script/color.php#color_names">color |
names</ulink> or HTML/CSS-style color specifications |
(<replaceable class="parameter">#RRGGBB<optional>AA</optional></replaceable>, |
<replaceable class="parameter">#RGB<optional>A</optional></replaceable>).</para> |
<para>See <ulink url="http://www.imagemagick.org/script/color.php" /> |
for more details and additional formats.</para> |
</note> |
<tip> |
<para>The command <literal>$ <userinput>convert -list color</userinput></literal> |
prints a list of all known colour names.</para> |
</tip> |
<para><literal>fg_heading</literal> &emdash; File meta information. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
<para><literal>fg_tstamps</literal> &emdash; Timestamps. |
Default: <constant>White</constant> |
[≡ RGB(255,255,255)]</para> |
<para><literal>fg_sign</literal> &emdash; Footer. |
Default: <constant>Black</constant> |
[≡ RGB(0,0,0)]</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_all"><literal>font_all</literal></term> |
<listitem> |
<para>Sets the value of all <literal>font_</literal> variables at once |
(<xref linkend="term-font_heading" />, |
<xref linkend="term-font_sign" />, |
<xref linkend="term-font_title" /> and |
<xref linkend="term-font_tstamps" />)</para> |
<para>Additional details: Since 1.12.2</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-font_heading"><literal>font_heading</literal></term> |
<term id="term-font_sign"><literal>font_sign</literal></term> |
<term id="term-font_title"><literal>font_title</literal></term> |
<term id="term-font_tstamps"><literal>font_tstamps</literal></term> |
<listitem> |
<para>These variables control the fonts used in each section of the contact sheet.</para> |
<para><literal>font_heading</literal> &emdash; File meta information. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_tstamps</literal> &emdash; Used for timestamps over the thumbnails. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
<para><literal>font_sign</literal> &emdash; Footer / signature. |
Default: <constant>DejaVu-Sans-Book</constant></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-format"><literal>format</literal></term> |
<listitem> |
<para>Output file format</para> |
<para>Default: <literal>png</literal></para> |
<note> |
<para>Should match the extension of a format known by <application>ImageMagick</application>.</para> |
</note> |
<para>Related command-line options: |
<option>-j</option>, <option>--jpeg</option> and |
<option>--jpeg2</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-getopt"><literal>getopt</literal></term> |
<listitem> |
<para><acronym>GNU</acronym> <command>getopt</command> command</para> |
<para>Default: <literal>getopt</literal></para> |
<warning> |
<para>The <command>getopt</command> command name must be set correctly or vcs won't work.</para> |
<para>Must be a version compatible with <acronym>GNU</acronym> syntax.</para> |
<para>Can only be set in configuration files (i.e. not from the command-line).</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-height"><literal>height</literal></term> |
<listitem> |
<para>Height of individual captures.</para> |
<para>Can be a fixed number of pixels or a percentage.</para> |
<para>The default is the same as input i.e. <literal>100%</literal>.</para> |
<para>Equivalent command-line option: <option>-H</option>, <option>--height</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-interval"><literal>interval</literal></term> |
<listitem> |
<para>Interval between captures, when the mode of operation is to capture |
at fixed intervals.</para> |
<para>Accepts the same format as any option accepting times, see &vcsmanpage; for details |
on the acceptable syntax.</para> |
<para>Default: <literal>300</literal> (≡ 5 minutes).</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-i</option> or <option>--interval</option>), |
changing the value of <symbol>interval</symbol> doesn't automatically |
switch modes to capture at intervals.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-i</option>, <option>--interval</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_filenames"><literal>nonlatin_filenames</literal></term> |
<listitem> |
<para>Enables or disables the usage of an alternate font to print |
filenames in the contact sheet meta-information section.</para> |
<para>Set to <literal>1</literal> to use <xref linkend="term-nonlatin_font" /> to print filenames.</para> |
<para>Default: <literal>0</literal> |
⇒ use the standard font, <xref linkend="term-font_heading"/>.</para> |
<para role="aside">Since 1.12.2</para> |
<para>Equivalent command-line option: <option>--nonlatin</option>, <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-nonlatin_font"><literal>nonlatin_font</literal></term> |
<listitem> |
<para>Font used for non-Latin filenames when <xref linkend="term-nonlatin_filenames" /> |
is enabled.</para> |
<para>Default: (picked automatically)</para> |
<note> |
<para>This font is, when possible, picked automatically.</para> |
<para>Can be set manually with the <option>-Ik</option> or <option>-Ij</option> option.</para> |
</note> |
<para>Equivalent command-line option: <option>-Ik</option>, <option>-Ij</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-numcaps"><literal>numcaps</literal></term> |
<listitem> |
<para>Number of captures, when the mode of operation is to do a fixed |
number of captures.</para> |
<para>Default: <literal>16</literal>.</para> |
<note> |
<para>Unlike its command-line counterpart (<option>-n</option> or <option>--numcaps</option>), |
changing the value of <symbol>numcaps</symbol> doesn't automatically |
switch modes to do a fixed number of captures.</para> |
<para>The mode of operation is controlled by <xref linkend="term-timecode_from" />.</para> |
</note> |
<para>Equivalent command-line option: <option>-n</option>, <option>--numcaps</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-padding"><literal>padding</literal></term> |
<listitem> |
<para>Number of pixels between captures when placed in the contact sheet.</para> |
<para>Default: <literal>2</literal></para> |
<para>Related command-line option: <option>-dp</option>, <option>--disable padding</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-plain_messages"><literal>plain_messages</literal></term> |
<listitem> |
<para>Allows disabling colourised feedback to the console.</para> |
<para>Set to <literal>1</literal> to print plain, monochrome, feedback.</para> |
<para>Default: <literal>0</literal> (≡ don't disable colours).</para> |
<para>Related command-line option: <option>-Wc</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-profiles"><literal>profiles</literal></term><!-- since 1.13 --> |
<listitem> |
<para>Loads profile(s).</para> |
<para>Its value must be a profile name or a comma-separated list of profile names.</para> |
<informalexample> |
<para>Example: |
<literal>profiles=<symbol>white</symbol>,<symbol>mosaic</symbol></literal> |
will load the <literal>white</literal> and <literal>mosaic</literal> profiles. |
</para> |
</informalexample> |
<para>Default: (empty).</para> |
<para>Equivalent command-line option: <option>-p</option>, <option>--profile</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-pts_meta"><literal>pts_meta</literal></term> |
<term id="term-pts_sign"><literal>pts_sign</literal></term> |
<term id="term-pts_title"><literal>pts_title</literal></term> |
<term id="term-pts_tstamps"><literal>pts_tstamps</literal></term> |
<listitem> |
<para>These variables control font size of each section in the contact sheet.</para> |
<para>These sizes are expressed in <emphasis>points</emphasis>.</para> |
<para><literal>pts_meta</literal> &emdash; File meta-information. |
Default: <literal>14</literal></para> |
<para><literal>pts_title</literal> &emdash; Title (with option <option>-T</option>). |
Default: <literal>33</literal>.</para> |
<para><literal>pts_tstamps</literal> &emdash; Timestamps. |
Default: <literal>14</literal>. |
<note> |
<para>The value of <symbol>pts_tstamps</symbol> is reduced for smaller captures.</para> |
</note> |
</para> |
<para><literal>pts_sign</literal> &emdash; Footer/signature. |
Default: <literal>10</literal></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-quality"><literal>quality</literal></term> |
<listitem> |
<para>Image quality (level of compression) when outputting to lossy formats.</para> |
<para><literal>0</literal> to <literal>100</literal>, with <literal>100</literal> |
being the best quality (the least compression).</para> |
<para>Default: <literal>92</literal>.</para> |
<note> |
<para>This value only affects the final image.</para> |
</note> |
</listitem> |
</varlistentry> |
<!-- GONE in 1.13 |
<varlistentry> |
<term id="term-safe_rename_pattern"><literal>safe_rename_pattern</literal></term> |
<listitem> |
<para>Pattern used for output files to avoid overwriting existing files.</para> |
<para>Default: <literal>%b-%N.%e</literal></para> |
<para>%b: Basename</para> |
<para>%N: Incremental number</para> |
<para>%e: extension</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-shoehorned"><literal>shoehorned</literal></term> |
<listitem> |
<para>Inserts additional parameters into ffmpeg or mplayer capture commands</para> |
<warning> |
<para>Scheduled for removal in 1.13</para> |
</warning> |
</listitem> |
</varlistentry> |
--> |
<varlistentry> |
<term id="term-signature"><literal>signature</literal></term> |
<listitem> |
<para>Text before the user name in the footer.</para> |
<para>Default: <literal>"Preview created by"</literal>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stderr"><literal>stderr</literal></term> |
<listitem> |
<para>Standard error of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stderr</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-stdout"><literal>stdout</literal></term> |
<listitem> |
<para>Standard output of programs when probing and capturing is sent here.</para> |
<para>Default: <filename class="devicefile">/dev/null</filename>.</para> |
<note> |
<para>Setting it to <filename class="devicefile">/dev/stdout</filename> to |
will return capturer programs to their normal behaviour.</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-timecode_from"><literal>timecode_from</literal></term> |
<listitem> |
<para>Controls the main mode of operation: capture at intervals or capture |
a fixed number of snapshots.</para> |
<para>Possible values are <literal><symbol>$TC_INTERVAL</symbol></literal> to |
capture at intervals (will use <xref linkend="term-interval" />), |
and <literal><symbol>$TC_NUMCAPS</symbol></literal> to capture a fixed |
number of images (will use <xref linkend="term-numcaps" />).</para> |
<para>Default: <literal><symbol>$TC_INTERVAL</symbol></literal>.</para> |
<note> |
<para>This setting is affected by command-line options <option>-i</option> |
and <option>-n</option>.</para> |
</note> |
<para>Related command-line options: |
<option>-i</option>, <option>--interval</option> and |
<option>-n</option>, <option>--numcaps</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-user"><literal>user</literal></term> |
<listitem> |
<para>User name for the footer's signature.</para> |
<para>Default: <command>$(id -un)</command> (≡ system user name).</para> |
<para>Related command-line options: |
<option>-u</option>, <option>--user</option> and |
<option>-U</option>, <option>--fullname</option> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term id="term-verbosity"><literal>verbosity</literal></term> |
<listitem> |
<para>Verbosity level.</para> |
<para>Possible values: |
<segmentedlist> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Value</segtitle> |
<segtitle>Meaning</segtitle> |
<seglistitem> |
<seg><literal><symbol>$V_ALL</symbol></literal></seg> |
<seg>Print everything. Equivalent to <symbol>$V_NOTICE</symbol>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_NONE</symbol></literal></seg> |
<seg>Print no feedback at all. Equivalent to command-line option <option>-qq</option>.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_ERROR</symbol></literal></seg> |
<seg>Print only errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_WARN</symbol></literal></seg> |
<seg>Print warnings and errors.</seg> |
</seglistitem> |
<seglistitem> |
<seg><literal><symbol>$V_INFO</symbol></literal></seg> |
<seg>Print informational messages, warnings and errors. |
This encompasses all messages, so it is equivalent to <symbol>$V_ALL</symbol>.</seg> |
</seglistitem> |
</segmentedlist> |
</para> |
<para>Default: <literal><symbol>$V_ALL</symbol></literal>.</para> |
<para>Related command-line option: <option>-q</option>, <option>--quiet</option>.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/docs/src/vcs.conf.man.xml |
---|
0,0 → 1,203 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id: vcs.conf.man.xml 2342 2011-09-01 13:19:47Z toni $ |
See vcs.man.xml for comments on docbook+man handling. |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY title "vcs User Manual"> |
<!ENTITY package "vcs.conf"> |
<!ENTITY section "5"> |
<!ENTITY emdash "—"> |
<!ENTITY equiv "≡"> |
<!ENTITY rArr "⇒"> |
<!ENTITY vcsmanpage "<citerefentry><refentrytitle>vcs</refentrytitle><manvolnum>1</manvolnum></citerefentry>"> |
<!-- |
XInclude trickery |
This voodoo is only required for the file to validate, it can be used |
by e.g. xsltproc without all of this |
Reference: http://www.sagehill.net/docbookxsl/ValidXinclude.html#XincludeDTD |
--> |
<!-- Define the xi:include and xi:fallback elements --> |
<!ELEMENT xi:include (xi:fallback?) > |
<!ATTLIST xi:include |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" |
href CDATA #IMPLIED |
parse (xml|text) "xml" |
xpointer CDATA #IMPLIED |
encoding CDATA #IMPLIED |
accept CDATA #IMPLIED |
accept-language CDATA #IMPLIED > |
<!ELEMENT xi:fallback ANY> |
<!ATTLIST xi:fallback |
xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude" > |
<!-- |
Add xi:include to the list of possible children of <refsect1> |
See http://www.oasis-open.org/docbook/xml/4.5/dbhierx.mod for the DTD |
module that defines which elements are allowed inside which. |
Can't allow xi:include in arbitrary places inside <refentry> |
--> |
<!ENTITY % local.refcomponent.mix "| xi:include"> |
]><!--/!DOCTYPE--> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev: 2342 $</releaseinfo> |
<!--<date>$Date: 2011-09-01 15:19:47 +0200 (dj, 01 set 2011) $</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&package;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>vcs configuration file</refpurpose> |
</refnamediv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para>This manual page describes the format and available settings |
in configuration and profile files for |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
<para>There's two types of files that follow this syntax: |
<link linkend="configfiles">configuration files</link> |
(see <xref linkend="configfiles"/>) |
and <link linkend="profiles">profiles</link> |
(see <xref linkend="profiles"/>). They'll be called collectively |
<emphasis>settings files</emphasis> in this manual page.</para> |
<para>Configuration files are meant to be loaded by default, intended to |
set user's preferred options, while |
profiles are meant to be loaded on-demand, intended to allow |
different parallel sets of settings.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="syntax"> |
<title>SYNTAX</title> |
<para>Settings files contain a series of |
<replaceable>SETTING</replaceable>=<replaceable>VALUE</replaceable> |
assignments. |
</para> |
<para>Comments can be included by preceding `<literal>#</literal>' to them.</para> |
<refsect2 id="metainfo"> |
<title>META-INFORMATION</title> |
<para>Meta-information fields can be contained in comments. |
They are written as '<literal>vcs:<replaceable>FIELDNAME</replaceable>:</literal>'.</para> |
<para>Currently supported meta-information fields:</para> |
<variablelist> |
<varlistentry> |
<term><literal>vcs:conf:</literal></term> |
<listitem><para>Marks a file as following this format.</para> |
<para>Files without this field will be rejected. |
<footnote> |
<para><filename>./vcs.conf</filename> won't be rejected if this |
field is missing, though it's preferable to include it |
to be ease moving the file to a different location or |
turning it into a profile.</para> |
</footnote> |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><literal>vcs:desc:</literal> <replaceable>DESCRIPTION</replaceable></term> |
<listitem><para>Describes this particular file's purpose, |
it is shown e.g. when listing available profiles. |
</para> |
<para>It is currently ignored for configuration files.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect2><!--/META-INFORMATION--> |
<refsect2 id="syntax-example"> |
<title>SYNTAX EXAMPLE</title> |
<programlisting># vcs:conf: |
# vcs:desc: White-on-black |
bg_all=black # Black background |
fg_all=white # White foreground</programlisting> |
</refsect2><!--/SYNTAX EXAMPLE--> |
</refsect1><!--/SYNTAX--> |
<refsect1 id="configfiles"> |
<title>CONFIGURATION FILES</title> |
<para>There's three configuration files loaded by default if present, in order:</para> |
<itemizedlist> |
<listitem><para><filename>/etc/vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/.vcs.conf</filename></para></listitem> |
<listitem><para><filename><envar>${HOME}</envar>/vcs/vcs.conf</filename></para></listitem> |
</itemizedlist> |
<para>Every file in this list overrides the previous when it |
re-defines a setting.</para> |
<para>Configuration files can be loaded manually off of any path by using the |
<option>--config <replaceable>FILENAME</replaceable></option> option.</para> |
</refsect1><!--/CONFIGURATION FILES--> |
<refsect1 id="profiles"> |
<title>PROFILE FILES</title> |
<para>No profile is loaded by default.</para> |
<para>Profiles are searched in three possible locations, in order:</para> |
<itemizedlist id="profile-paths"> |
<listitem><para><filename class="directory"><envar>${HOME}</envar>/.vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/local/share/vcs/profiles/</filename></para></listitem> |
<listitem><para><filename class="directory">/usr/share/vcs/profiles/</filename></para></listitem> |
</itemizedlist> |
<para>Only the first profile for each name will be considered. |
Profiles with the same name will be hidden.</para> |
<para><literal>$ <command>vcs --profile :list</command></literal></para> |
<para>can be used to get a list of available profiles.</para> |
<para>Profiles can only be loaded from the <link linkend="profile-paths">listed |
paths</link>.</para> |
</refsect1><!--/PROFILE FILES--> |
<refsect1> |
<title>SETTINGS</title> |
<para>This list details the available settings. Settings are listed in |
alphabetical order.</para> |
<para>A list of available settings, grouped by categories, is also kept |
online at <ulink url="http://p.outlyer.net/dox/vcs:conf_files" /></para> |
<xi:include |
xmlns:xi="http://www.w3.org/2001/XInclude" |
href="./settings.man.inc.xml" /> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<para> |
<citerefentry> |
<refentrytitle>vcs</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, |
<citerefentry> |
<refentrytitle>id</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry> |
</para> |
</refsect1><!--/SEE ALSO--> |
</refentry> |
<!-- vim:set ts=4 et: --> |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/docs/src/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev: 2333 $ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
columns=2 # Number of columns in the contact sheet (option -c) |
interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
format=png |
quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
signature=Preview created by |
disable_shadows=0 # Disable shadows by default (option -ds) |
disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
bg_heading=#afcd7a # Heading/meta-information section background colour |
fg_heading=Black # Heading font colour |
font_heading=DejaVu-Sans-Book # Heading font |
pts_heading=14 # Font size for heading |
bg_title=White # Background for the title (if activated with option -T) |
fg_title=Black # Title font colour |
font_title=$font_heading # Title font |
bg_contact=White # Background for the contact sheet |
bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
fg_tstamps=White # Timestamps font colour |
font_tstamps=$font_heading # Timestamps font |
pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
bg_sign=SlateGray |
fg_sign=Black # Font colour for the signature |
font_sign=$font_heading # Font for the signature |
pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
stdout=/dev/null |
stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
verbosity=$V_ALL |
# 1 disables colours in console output |
simple_feedback=0 |
debug=0 # When 1, enables debugging mode (option -D) |
getopt=getopt # GNU Getopt executable name |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/docs/src/vcs.man.xml |
---|
0,0 → 1,850 |
<?xml version="1.0" encoding="UTF-8" ?> |
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ |
<!-- |
$Id$ |
Useful Docbook References: |
- Creating DocBook Documents - List of elements |
<http://www.docbook.org/tdg5/en/html/ch02.html> |
- Writing with DocBook elements - Useful commands (elements) |
<http://www.ibiblio.org/godoy/sgml/docbook/howto/writing-docbook.html#WRITING-DOCBOOK-COMMANDS> |
- DocBook Guide for Authors of Geant4 User Manuals - Tag Mapping Table - (X)HTML vs. DocBook |
<http://geant4.web.cern.ch/geant4/workAreaUserDocKA/AuthorsInstruction/IntroDocBook.html#TagMap> |
- DocBook 5: The Definitive Guide (includes list of elements) |
<http://docbook.org/tdg51/en/html/docbook.html> |
Generation of man page: |
$ xmlto man manpage.xml |
OR |
$ xsltproc -''-nonet \ |
-''-param man.charmap.use.subset "0" \ |
-''-param make.year.ranges "1" \ |
-''-param make.single.year.ranges "1" \ |
/usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl \ |
manpage.xml |
Will generate vcs.1. |
View with: |
$ nroff -man vcs.1 | less |
or |
$ man vcs.1 |
Validation: xmllint -''-noout -''-valid manpage.xml |
Spellcheck: aspell -l en-GB -H check FILENAME.xml |
--> |
<!ENTITY firstname "Toni"> |
<!ENTITY surname "Corvera"> |
<!-- fullname could also be set to "&firstname; &surname;". --> |
<!ENTITY fullname "&firstname; &surname;"> |
<!ENTITY email "outlyer@gmail.com"> |
<!ENTITY section "1"> |
<!-- TITLE should be something like "User commands" or similar (see |
http://www.tldp.org/HOWTO/Man-Page/q2.html). --> |
<!ENTITY title "Video Contact Sheet *NIX User Manual"> |
<!ENTITY ucpackage "VCS"> |
<!ENTITY package "vcs"> |
<!ENTITY emdash "—"> |
<!ENTITY xrefinterval 'See the accepted syntax at <xref linkend="interval_format" />.'> |
]> |
<refentry lang="en-GB"> |
<refentryinfo> |
<title>&title;</title> |
<productname>&package;</productname> |
<author> |
<firstname>&firstname;</firstname> |
<surname>&surname;</surname> |
<contrib /> |
<!-- <contrib>VCS author.</contrib> --> |
<address> |
<email>&email;</email> |
<otheraddr> |
<ulink url="http://corvera.eu./" /> |
</otheraddr> |
</address> |
</author> |
<copyright> |
<year>2007-2011</year> |
<holder>&fullname;</holder> |
</copyright> |
<legalnotice> |
<para>Permission is granted to copy, distribute and/or modify this |
document under the terms of the GNU Lesser General Public License, |
Version 2 or (at your option) any later version published by |
the Free Software Foundation.</para> |
</legalnotice> |
<releaseinfo>$Rev$</releaseinfo> |
<!--<date>$Date$</date>--> |
</refentryinfo> |
<refmeta> |
<refentrytitle>&ucpackage;</refentrytitle> |
<manvolnum>§ion;</manvolnum> |
</refmeta> |
<refnamediv> |
<refname>&package;</refname> |
<refpurpose>create contact sheets from videos</refpurpose> |
</refnamediv> |
<refsynopsisdiv> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><replaceable class="parameter">FILE</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable class="parameter">FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT1</replaceable></option></arg> |
<arg choice="opt"><option>--output=<replaceable>OUTPUT2</replaceable></option></arg> |
<arg choice="opt"><option>...</option></arg> |
<arg choice="plain"><replaceable>INPUT1</replaceable></arg> |
<arg choice="opt" rep="repeat"><replaceable>INPUT2</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<group choice="opt"> |
<arg><option>-n <replaceable>20</replaceable></option></arg> |
<arg><option>-i <replaceable>1m</replaceable></option></arg> |
</group> |
<arg><option>-c <replaceable>4</replaceable></option></arg> |
<arg><option>-H <replaceable>120</replaceable></option></arg> |
<arg rep="repeat"></arg> |
<arg choice="plain" rep="repeat"><replaceable>FILE</replaceable></arg> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<!-- Help/test options. |
They stop the program after outputting their related information. --> |
<group choice="opt"> |
<arg choice="plain"> |
<group choice="req"> |
<arg choice="plain"><option>-h</option></arg> |
<arg choice="plain"><option>--help</option></arg> |
</group> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>--fullhelp</option></arg> |
</arg> |
<arg choice="plain"> |
<arg choice="plain"><option>-DD</option></arg> |
</arg> |
</group> |
</cmdsynopsis> |
<cmdsynopsis> |
<command>&package;</command> |
<arg choice="opt" rep="repeat"><replaceable class="option">OPTION</replaceable></arg> |
<arg choice="plain"><option>--generate</option> |
<group choice="req"> |
<arg choice="plain">config</arg> |
<arg choice="plain">profile</arg> |
</group> |
</arg> |
</cmdsynopsis> |
</refsynopsisdiv> |
<refsect1 id="description"> |
<title>DESCRIPTION</title> |
<para><command>&package;</command> creates a preview |
image from videos in a contact sheet-like format (i.e. captures from |
different frames in the video are placed in a mosaic).</para> |
<para>By default the output file will be named like the input file plus the |
png extension. Example: "<filename>file.avi</filename>" will produce |
a contact sheet in the file "<filename>file.avi.png</filename>".</para> |
<para>The default mode of operation is to obtain captures every five minutes in the |
video, so the amount of captures will vary with each file. The command-line |
argument <parameter>--numcaps</parameter> (<parameter>-n</parameter>) can be used |
to change this behaviour or alternatively a configuration file might |
be used to change the mode of operation (see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>). |
</para> |
<para>This manual page documents <command>&package;</command>, |
further documentation can be found in the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> site.</para> |
</refsect1><!--/DESCRIPTION--> |
<refsect1 id="options"> |
<title>OPTIONS</title> |
<para>The program follows the usual GNU command line syntax, |
with long options starting with two dashes (`-'). A summary of |
options is included below.</para> |
<variablelist> |
<varlistentry> |
<term><option>-n <replaceable>number</replaceable></option></term> |
<term><option>--numcaps=<replaceable>number</replaceable></option></term> |
<listitem> |
<para>Fixes the number of captures to obtain.</para> |
<para>Sets the mode of operation to capture a fixed number of frames.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-i <replaceable>INTERVAL</replaceable></option></term> |
<term><option>--interval=<replaceable>INTERVAL</replaceable></option></term> |
<listitem> |
<para>Sets the interval between captures.</para> |
<para>Sets the mode of operation to capture at fixed intervals.</para> |
<para>The number of captures will depend on the video length.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-c <replaceable>NUMBER</replaceable></option></term> |
<term><option>--columns=<replaceable>NUMBER</replaceable></option></term> |
<listitem> |
<para>Number of columns in the contact sheet.</para> |
<para>The number of rows will depend on this value and the number of captures (there's no |
way to set the number of rows).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-H <replaceable>HEIGHT</replaceable></option></term> |
<term><option>--height=<replaceable>HEIGHT</replaceable></option></term> |
<listitem> |
<para>Height of captures.</para> |
<para>Can be a number (of pixels) or a percentage (of the video height).</para> |
<para>By default the same size as the video is used.</para> |
<note> |
<para>The width is derived from height and aspect ratio.</para> |
</note> |
<tip> |
<para><replaceable>HEIGHT</replaceable> x <replaceable>WIDTH</replaceable> |
can be manually forced by setting both <option>-H</option> and |
<option>-a</option>, e.g. <replaceable>640x480</replaceable>:</para> |
<para><literal>$ <command>vcs -a 640/480 -H 480 <replaceable><optional>...</optional></replaceable></command></literal></para> |
</tip> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-o <replaceable>FILENAME</replaceable></option></term> |
<term><option>--output=<replaceable>FILENAME</replaceable></option></term> |
<listitem> |
<para>Name of output file.</para> |
<para>By default the video file name plus the output |
format is used (e.g. "<filename>video.avi.png</filename>" |
for "<filename>video.avi</filename>").</para> |
<para>If an extension is provided, it will define the output format, otherwise |
PNG will be used. I.e. <filename>sheet.jpg</filename> will produce |
a JPEG file while <filename>sheet</filename> or |
<filename>sheet.png</filename> will produce a PNG file.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-h</option></term> |
<term><option>--help</option></term> |
<listitem> |
<para>Show summary of most common options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--fullhelp</option></term> |
<listitem> |
<para>Show summary of all options.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-a <replaceable>ASPECT</replaceable></option></term> |
<term><option>--aspect <replaceable>ASPECT</replaceable></option></term> |
<listitem> |
<para>Aspect ratio.</para> |
<para>Accepts a floating point number or a fraction.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-f <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--from <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set starting time. No captures will be made before this <replaceable>TIMESTAMP</replaceable>.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-t <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--to <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Set ending time. No captures will be made after this TIMESTAMP.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-T <replaceable>TITLE</replaceable></option></term> |
<term><option>--title <replaceable>TITLE</replaceable></option></term> |
<listitem> |
<para>Add a title above the captures.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j</option></term> |
<term><option>--jpeg</option></term> |
<listitem> |
<para>Output file in JPEG format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-j2</option></term> |
<term><option>--jpeg2</option></term> |
<term><option>--jpeg=2</option></term> |
<listitem> |
<para>Output file in JPEG 2000 format.</para> |
<para>The default output format is PNG.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-V</option></term> |
<term><option>--dvd</option></term> |
<listitem> |
<para>DVD mode.</para> |
<para>In this mode the input files must be the DVD |
device(s) or ISO(s).</para> |
<para>When in DVD mode all input files must be DVDs.</para> |
<note> |
<para>Implies <option>-A</option> (auto aspect ratio).</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--dvd-title <replaceable>TITLENUM</replaceable></option></term> |
<listitem> |
<para>DVD title to use.</para> |
<para>Using 0 (the default) will use the longest title.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-M</option></term> |
<term><option>--mplayer</option></term> |
<listitem> |
<para>Use Mplayer to capture.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-F</option></term> |
<term><option>--ffmpeg</option></term> |
<listitem> |
<para>Use FFmpeg to capture.</para> |
<para>This is the default, except in DVD mode.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-E <replaceable>OFFSET</replaceable></option></term> |
<term><option>--end-offset <replaceable>OFFSET</replaceable></option></term> |
<listitem> |
<para>This amount of time is ignored from the end of the video.</para> |
<para>This value is not used when a explicit ending time is set (<option>--to</option>).</para> |
<para>Accepted formats:</para> |
<itemizedlist spacing="compact"> |
<listitem><para>Time stamp (&xrefinterval;)</para></listitem> |
<listitem><para>Percentage of video length.</para></listitem> |
</itemizedlist> |
<para>The default is 5.5%.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-q</option></term> |
<term><option>--quiet</option></term> |
<listitem> |
<para>Don't print progress messages just errors.</para> |
<para>Repeat to mute completely, even on error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-d <replaceable>FEATURE</replaceable></option></term> |
<term><option>--disable <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Disable some default functionality.</para> |
<para>Features that can be disabled are:</para> |
<itemizedlist spacing="compact"> |
<listitem> |
<para><replaceable>timestamps</replaceable>: use <option>-d<replaceable>t</replaceable></option> or |
<option>--disable <replaceable>timestamps</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>shadows</replaceable>: use <option>-d<replaceable>s</replaceable></option> |
or <option>--disable <replaceable>shadows</replaceable></option></para> |
</listitem> |
<listitem> |
<para><replaceable>padding</replaceable>: use <option>-d<replaceable>p</replaceable></option> |
or <option>--disable <replaceable>padding</replaceable></option></para> |
</listitem> |
</itemizedlist> |
<note> |
<para>Shadows introduce some extra padding</para> |
</note> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-A</option></term> |
<term><option>--autoaspect</option></term> |
<listitem> |
<para>Try to guess aspect ratio from resolution.</para> |
<para>A rude hard-coded method is used based only on known common dimensions.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-e</option></term> |
<term><option>-e<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<term><option>--extended=<optional><replaceable>FACTOR</replaceable></optional></option></term> |
<listitem> |
<para>Enables extended mode and optionally sets the extended factor.</para> |
<para>When <replaceable>FACTOR</replaceable> is omitted, 4 is used, i.e. <option>-e</option> is the same as <option>-e4</option>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-l <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--highlight <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame found at <replaceable>TIMESTAMP</replaceable> as a highlight.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-m</option></term> |
<term><option>--manual</option></term> |
<listitem> |
<para>Manual mode.</para> |
<para>In this mode only timestamps indicated by the user are used (use in |
conjunction with <option>-S</option>).</para> |
<para>When using this option, <option>-i</option> and <option>-n</option> are ignored.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-S <replaceable>TIMESTAMP</replaceable></option></term> |
<term><option>--stamp <replaceable>TIMESTAMP</replaceable></option></term> |
<listitem> |
<para>Add the frame at <replaceable>TIMESTAMP</replaceable> to the set of captures.</para> |
<para>&xrefinterval;</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-u <replaceable>NAME</replaceable></option></term> |
<term><option>--user <replaceable>NAME</replaceable></option></term> |
<listitem> |
<para>Set the user name (included by default in the contact sheet's footer) |
to <replaceable>NAME</replaceable>.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-U</option></term> |
<term><option>--fullname</option></term> |
<listitem> |
<para>Use user's full/real name (e.g. John Smith) as set in the system's list of users |
(i.e. in <filename>/etc/passwd</filename> or through <command>getent</command>).</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-p <replaceable>PROFILE</replaceable></option></term> |
<term><option>--profile <replaceable>PROFILE</replaceable></option></term> |
<listitem> |
<para>Load profile named <replaceable>PROFILE</replaceable>.</para> |
<para>Profile names starting with ':' are reserved and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:list</replaceable> &emdash; Will list all profiles found in the |
system</para></listitem> |
</itemizedlist> |
<para>If <replaceable>PROFILE</replaceable> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-C <replaceable>CONFIG</replaceable></option></term> |
<term><option>--config <replaceable>CONFIG</replaceable></option></term> |
<listitem> |
<para>Load configuration file <filename><replaceable>CONFIG</replaceable></filename></para> |
<para>Configuration <emphasis>file names</emphasis> starting with ':' are reserved |
and have special meanings, currently:</para> |
<itemizedlist> |
<listitem><para><replaceable>:pwd</replaceable> &emdash; Will try to load |
<filename>./vcs.conf</filename>.</para> |
<para>This file has been loaded by default up to vcs v1.13</para></listitem> |
</itemizedlist> |
<para>If <filename><replaceable>CONFIG</replaceable></filename> doesn't exist, exit with error.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--generate <replaceable>config|profile</replaceable></option></term> |
<listitem> |
<para>Generate configuration or profile from the current settings and print it.</para> |
<para>All settings changed from the default, by either configuration, profiles or command-line |
options, will be included in the generated text.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-k <replaceable>MODE</replaceable></option></term> |
<term><option>--funky <replaceable>MODE</replaceable></option></term> |
<listitem> |
<para>Funky modes</para> |
<para>These are <emphasis>toy</emphasis> output modes in which the contact sheet |
gets a more informal look.</para> |
<caution> |
<para>Order <emphasis role="strong">IS IMPORTANT</emphasis>, it affects output.</para> |
<para>A bad order will produce a bad result.</para> |
</caution> |
<para>Many of these modes are random in nature so using the same mode twice |
will usually lead to very different results.</para> |
<para>Currently available <emphasis>funky modes</emphasis>:</para> |
<variablelist id="funkymodes"> |
<varlistentry> |
<term><replaceable>overlap</replaceable>: |
Use <option>-k<replaceable>o</replaceable></option> |
or <option>--funky <replaceable>overlap</replaceable></option></term> |
<listitem><para>Randomly overlap captures.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>rotate</replaceable>: |
Use <option>-k<replaceable>r</replaceable></option> |
or <option>--funky <replaceable>rotate</replaceable></option></term> |
<listitem><para>Randomly rotate each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photoframe</replaceable>: |
Use <option>-k<replaceable>f</replaceable></option> |
or <option>--funky <replaceable>photoframe</replaceable></option></term> |
<listitem><para>Adds a photo-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroidframe</replaceable>: |
Use <option>-k<replaceable>L</replaceable></option> |
or <option>--funky <replaceable>polaroidframe</replaceable></option></term> |
<listitem><para>Adds a polaroid picture-like white frame to each image.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>photos</replaceable>: |
Use <option>-k<replaceable>c</replaceable></option> |
or <option>--funky <replaceable>photos</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>photoframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kp -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>polaroid</replaceable>: |
Use <option>-k<replaceable>p</replaceable></option> |
or <option>--funky <replaceable>polaroid</replaceable></option></term> |
<listitem><para>Combination of <replaceable>rotate</replaceable>, |
<replaceable>polaroidframe</replaceable> and <replaceable>overlap</replaceable>.</para> |
<para>Same as <option>-kL -kr -ko</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>film</replaceable>: |
Use <option>-k<replaceable>i</replaceable></option> |
or <option>--funky <replaceable>film</replaceable></option></term> |
<listitem><para>Imitates filmstrip look.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>random</replaceable>: |
Use <option>-k<replaceable>x</replaceable></option> |
or <option>--funky <replaceable>random</replaceable></option></term> |
<listitem><para>Randomises colours and fonts.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>--anonymous</option></term> |
<listitem> |
<para>Disable the «Preview created by <replaceable>USERNAME</replaceable>» line in the footer.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Ij<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>-Ik<optional>=<replaceable>FONTNAME</replaceable></optional></option></term> |
<term><option>--nonlatin</option></term> |
<listitem> |
<para>Use an alternate font in the heading for the video file name.</para> |
<para>Required to display correctly file names in some languages with non-Latin |
alphabets (Chinese, Japanese, Hangul, Cyrillic, ...).</para> |
<para>When no font name is given, a reasonable choice will be made if possible.</para> |
<para>When <replaceable>FONTNAME</replaceable> is given, it can be either |
a font name:</para> |
<para><literal>$ <command>vcs -Ij=Sazanami-Mincho-Regular <filename>file.avi</filename></command></literal></para> |
<para>Or a font file name:</para> |
<para><literal>$ <command>vcs -Ij=<filename>/usr/share/fonts/ttf/ttf-japanese-mincho.ttf</filename> <filename>file.avi</filename></command></literal></para> |
<para>A list of available fonts and their names can be obtained with the command |
<command>identify <option>-list font</option></command></para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-O <replaceable>SETTING=VALUE</replaceable></option></term> |
<term><option>--override <replaceable>SETTING=VALUE</replaceable></option></term> |
<listitem> |
<para>Changes the value of SETTING to VALUE, |
as if it was set from a configuration file.</para> |
<para>Some settings can only be changed through configuration files or overrides, while |
others have associated command-line options.</para> |
<para><replaceable>VALUE</replaceable> can be quoted to include spaces:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING="my value" <replaceable>...</replaceable></command></literal></para> |
<para><replaceable>VALUE</replaceable> can also refer to some other setting:</para> |
<para><literal>$ <command>vcs -O SOME_SETTING='$SOME_OTHER_SETTING' <replaceable>...</replaceable></command></literal></para> |
<para>See <citerefentry><refentrytitle>vcs.conf</refentrytitle> <manvolnum>5</manvolnum></citerefentry> |
and the |
<ulink url="http://p.outlyer.net/dox/vcs">online documentation</ulink> for |
a list of possible <replaceable>SETTING</replaceable>s.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W <replaceable>WORKAROUND</replaceable></option></term> |
<listitem> |
<para>Enables one of the known workarounds for problematic files, or some tweak:</para> |
<variablelist id="workarounds"> |
<varlistentry> |
<term><option>-W<replaceable>s</replaceable></option></term> |
<listitem><para>Increase length of safe measuring (try harder).</para> |
<para>Repeat to increase further.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>S</replaceable></option></term> |
<listitem><para>Scan all video, if required, to get a valid length measuring.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>p</replaceable></option></term> |
<listitem><para>Increase safe measuring precision (i.e. halve the probe stepping).</para> |
<para>Repeat to increase further.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>P</replaceable></option></term> |
<listitem><para>Inverse of <option>-Wp</option>.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>o</replaceable></option></term> |
<listitem><para>Change FFmpeg's arguments order, might work |
with some files that fail otherwise.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-W<replaceable>c</replaceable></option></term> |
<listitem><para>Disable colour in console messages.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="debug_options"> |
<title>DEBUGGING OPTIONS</title> |
<variablelist> |
<varlistentry> |
<term><option>-R <replaceable>FILE</replaceable></option></term> |
<term><option>--randomsource <replaceable>FILE</replaceable></option></term> |
<listitem> |
<para>Use FILE as a source for "random" values.</para> |
<para>They won't be random anymore, so two runs with the same source and same |
arguments will produce the same output in modes which use randomisation |
(e.g. the modes triggered by <option>-k <replaceable>photos</replaceable></option> |
and <option>-k <replaceable>polaroid</replaceable></option>). |
</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-D</option></term> |
<listitem> |
<para>Debug mode.</para> |
<para>Used to test features/integrity. It:</para> |
<itemizedlist> |
<listitem><para>Prints the input command line</para></listitem> |
<listitem><para>Sets the title to reflect the command line</para></listitem> |
<listitem><para>Does a basic test of consistency</para></listitem> |
<listitem><para>Prints a trace of all internal functions as they are called</para></listitem> |
</itemizedlist> |
<para>Repeat to just test consistency and exit</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><option>-Z <replaceable>FEATURE</replaceable></option></term> |
<term><option>--undocumented <replaceable>FEATURE</replaceable></option></term> |
<listitem> |
<para>Testbed for experimental and debugging features. Some <replaceable>FEATURE</replaceable>s |
might be <emphasis>promoted</emphasis> in the future to actual command-line |
options.</para> |
<para><replaceable>FEATURE</replaceable>s here are rough implementations |
and have no error-handling.</para> |
<para><replaceable>FEATURE</replaceable> names can be added or removed |
in every version, silently, so don't rely on them.</para> |
<para>Useful for end-users:</para> |
<variablelist> |
<varlistentry> |
<term><replaceable>idonly</replaceable></term> |
<listitem><para>Prints the file probing/identification information and exit.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>display</replaceable></term> |
<listitem><para>Display the generated contact sheet.</para></listitem> |
</varlistentry> |
<varlistentry> |
<term><replaceable>discard</replaceable></term> |
<listitem><para>Remove the created file on exit.</para></listitem> |
</varlistentry> |
</variablelist> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="files"> |
<title>FILES</title> |
<variablelist> |
<varlistentry> |
<term><filename>/etc/vcs.conf</filename></term> |
<listitem> |
<para>The system-wide configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
<varlistentry> |
<term><filename>${HOME}/.vcs.conf</filename></term> |
<term><filename>${HOME}/.vcs/vcs.conf</filename></term> |
<listitem> |
<para>The per-user configuration file to control the |
behaviour of <application>&package;</application>. See |
<citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry> for further details.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="interval_format"> |
<title>INTERVALS</title> |
<para> |
Intervals and timestamps can be specified in seconds or in a human-readable format |
that follows the syntax |
<programlisting><replaceable>HOURS</replaceable>h<replaceable>MINUTES</replaceable>m<replaceable>SECONDS</replaceable>s.<replaceable>MILLISECONDS</replaceable></programlisting> |
where each element is optional.</para> |
<para>See <ulink url="http://p.outlyer.net/dox/vcs:time_syntax" /> for more details.</para> |
<table> |
<title>Interval syntax examples</title> |
<tgroup cols="3"> |
<thead> |
<row> |
<entry>Example</entry> |
<entry>Equivalence</entry> |
<entry>Standard time format</entry> |
</row> |
</thead> |
<tbody> |
<row> |
<entry>1h30m30</entry><entry>1h30m30s.00</entry><entry>1:30:30.00</entry> |
</row> |
<row> |
<entry>30</entry><entry>0h0m30s.00</entry><entry>0:00:30.00</entry> |
</row> |
<row> |
<entry>3600</entry><entry>1h0m0s.00</entry><entry>1:00:00.00</entry> |
</row> |
</tbody> |
</tgroup> |
</table> |
</refsect1> |
<refsect1 id="environment"> |
<title>ENVIRONMENT</title> |
<variablelist> |
<varlistentry> |
<term><envar>TEMPDIR</envar></term> |
<listitem> |
<para>Fallback temporary directory when |
<filename class="directory">/dev/shm</filename> is not available. |
Due to the big size of temporary files, it is recommended to use |
a temporary directory on a fast filesystem.</para> |
</listitem> |
</varlistentry> |
</variablelist> |
</refsect1> |
<refsect1 id="diagnostics"> |
<title>DIAGNOSTICS</title> |
<para>The default verbosity level will print <package>&package;</package>' progress |
and any errors or warnings on <filename class="devicefile">stderr</filename>.</para> |
<para><option>--quiet</option> can be used to reduce verbosity.</para> |
<para>The verbosity level and where to direct <filename class="devicefile">stderr</filename> |
can be controlled through configuration files, see <citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>. |
</para> |
<para><command>&package;</command> provides some return codes, they follow |
the semi-standardised values defined in |
<filename class="headerfile">sysexits.h</filename>:</para> |
<segmentedlist> |
<!-- Force table-style presentation instead of list with repeated |
headings. |
<http://www.docbook.org/tdg/en/html/segmentedlist.html> |
--> |
<?dbhtml list-presentation="table"?> |
<?dbfo list-presentation="table"?> |
<segtitle>Code</segtitle> |
<segtitle>Diagnostic</segtitle> |
<seglistitem> |
<seg><errorcode> 0</errorcode> (<errorcode>EX_OK</errorcode>)</seg> |
<seg>Program exited successfully.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>64</errorcode> (<errorcode>EX_USAGE</errorcode>)</seg> |
<seg>Error in the arguments.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>66</errorcode> (<errorcode>EX_NOINPUT</errorcode>)</seg> |
<seg>Can't access some input file or it has an incorrect format.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>69</errorcode> (<errorcode>EX_UNAVAILABLE</errorcode>)</seg> |
<seg>Unsatisfied dependency.</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>70</errorcode> (<errorcode>EX_SOFTWARE</errorcode>)</seg> |
<seg>Internal inconsistency (bug).</seg> |
</seglistitem> |
<seglistitem> |
<seg><errorcode>73</errorcode> (<errorcode>EX_CANTCREAT</errorcode>)</seg> |
<seg>Error creating temporary or output files.</seg> |
</seglistitem> |
</segmentedlist> |
</refsect1> |
<refsect1 id="bugs"> |
<!-- Or use this section to tell about upstream BTS. --> |
<title>BUGS</title> |
<para>The upstream bug tracker system can be found |
at <ulink url="http://b.outlyer.net"/>, bugs can be reported |
through the <ulink url="http://b.outlyer.net"><acronym>BTS</acronym></ulink> |
or through e-mail addressed at <email>outlyer@gmail.com</email>.</para> |
<note> |
<para>Recent versions of <application>ImageMagick</application>, |
<application>mplayer</application> and |
<application>ffmpeg</application> should be used |
for maximum compatibility.</para> |
</note> |
<para>Most testing is done on <systemitem class="osname">Debian Sid</systemitem>, plus |
<systemitem class="osname">FreeBSD</systemitem> for <acronym>BSD</acronym> compatibility |
tests.</para> |
<para>Using <acronym>OS</acronym>es other than |
<systemitem class="osname">Debian Sid</systemitem> |
or <systemitem class="osname">FreeBSD</systemitem> |
might uncover bugs and produce incompatibilities unknown to the author. |
</para> |
</refsect1> |
<refsect1 id="see_also"> |
<title>SEE ALSO</title> |
<!-- In alpabetical order. --> |
<para><citerefentry> |
<refentrytitle>vcs.conf</refentrytitle> |
<manvolnum>5</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>convert</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>ffmpeg</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry>, <citerefentry> |
<refentrytitle>mplayer</refentrytitle> |
<manvolnum>1</manvolnum> |
</citerefentry></para> |
</refsect1> |
</refentry> |
<!-- vim:set ts=4 et: --> |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/docs/src/flatten_settings_xml.bash |
---|
0,0 → 1,33 |
#!/bin/bash |
# |
# This file inlines file included through the XIncludes system. |
# This workaround is used to work with jade (used in PDF |
# creation) since, AFAIK, it doesn't support XIncludes. |
# |
SETTINGS_XML=vcs.conf.man.xml |
IN=0 |
# Preserve leading white-space by reducing IFS to only '\n': |
IFS='\ |
' |
while read -ers line ; do |
if grep -q '<xi:include' <<<"$line" ; then |
IN=1 |
elif [[ $IN -eq 1 ]]; then |
if grep -q 'href=' <<<"$line" ; then |
toinclude=$(sed -r 's/.*href="([^"]*)".*/\1/'<<<"$line") |
docstart=$(egrep -n '^]>$' $toinclude | cut -d':' -f1) |
let 'docstart++' |
sed -n "$docstart,\$p" "$toinclude" |
fi |
fi |
if [[ $IN -ne 1 ]]; then |
echo "$line" |
fi |
if [[ $IN -eq 1 ]] && grep -q '/>' <<<"$line"; then |
IN=0 |
fi |
done <${SETTINGS_XML} |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/docs/GNUmakefile |
---|
0,0 → 1,105 |
# |
# $Id$ |
# |
# This Makefile uses GNU Make syntax. |
# The distribution tarball should already include the files generated |
# here so there's usually no need to use it. |
# |
distdir:=. |
srcdir=src |
ALL=$(addprefix $(distdir)/,vcs.1 vcs.conf.5 \ |
$(addprefix vcs.man,.html .xhtml .pdf) \ |
$(addprefix vcs.conf.man,.html .xhtml .pdf) \ |
) |
INTERMEDIATE=$(addprefix $(srcdir)/, \ |
$(addsuffix .tex, vcs.man vcs.conf.man) \ |
) |
ifeq ($(shell uname),FreeBSD) |
DOCBOOK_XSL:=/usr/local/share/xsl/docbook |
endif |
DOCBOOK_XSL?=/usr/share/xml/docbook/stylesheet/docbook-xsl |
# Common part of command to convert docbook to man |
DOCBOOK_TO_MAN=xsltproc -o $(distdir)/ -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/manpages/docbook.xsl |
all: $(ALL) |
clean: |
$(RM) $(ALL) $(INTERMEDIATE) |
# man2html produces output closer to man and better formatted but |
# easily broken while xsltproc produces cleaner, more robust, and |
# cross-referenced output |
# sed post processing: |
# add CSS link |
# obfuscate mailto: links |
# obfuscate emails |
$(distdir)/vcs.%.xhtml: $(srcdir)/vcs.%.xml |
xsltproc -nonet \ |
--xinclude \ |
-param man.charmap.use.subset "0" \ |
-param make.year.ranges "1" \ |
-param make.single.year.ranges "1" \ |
$(DOCBOOK_XSL)/xhtml/docbook.xsl \ |
"$<" > "$@" || ( $(RM) "$@" && false ) |
sed -i \ |
-e 's!</head>!<link rel="stylesheet" type="text/css" href="man.css"/></head>!' \ |
-e 's/mailto:\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/mailto:\1%40\2%2E\3/' \ |
-e 's/\([[:alnum:]]*\)@\([[:alnum:]]*\)\.\([[:alpha:]]*\)/\1\@\2\.\3/' \ |
"$@" |
# The xml.dcl file MUST be included in this order, after options and before inputs |
$(srcdir)/vcs.conf.man.tex: $(srcdir)/vcs.conf.man.xml |
cd $(srcdir) && bash flatten_settings_xml.bash > temp.xml || ( rm temp.xml && false ) |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
$(srcdir)/temp.xml || ( rm $(srcdir)/temp.xml && false ) |
$(RM) $(srcdir)/temp.xml |
$(srcdir)/vcs.man.tex: $(srcdir)/vcs.man.xml |
jade -E0 -t tex \ |
-d /usr/share/sgml/docbook/stylesheet/dsssl/modular/print/docbook.dsl \ |
-o "$@" \ |
/usr/share/sgml/declaration/xml.dcl \ |
"$<" >/dev/null |
$(distdir)/vcs.%.pdf: $(srcdir)/vcs.%.tex |
pdfjadetex -output-directory $(distdir) $< |
$(RM) $(addprefix $(distdir)/vcs.$(*), .log .aux .out) |
# Check all XML files for validity |
lint: |
# XML check |
find . -type f -name '*.xml' -print0 | \ |
xargs -0 xmllint -nonet --xinclude -noout --valid |
# XHTML check |
# Use `$(MAKE) xhtml' before running `$(MAKE) $@' to |
# actually validate XHTML |
find . -type f -name '*.xhtml' -exec bash -c "echo '[ {} ]' && tidy -utf8 -eq '{}'" \; |
xhtml: $(filter %.xhtml, $(ALL)) |
$(distdir)/vcs.man.html: $(distdir)/vcs.1 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.conf.man.html: $(distdir)/vcs.conf.5 |
man2html -r "$<" > "$@" |
$(distdir)/vcs.1: $(srcdir)/vcs.man.xml |
#xmlto -o `dirname $@`/ man $< |
$(DOCBOOK_TO_MAN) "$<" |
$(distdir)/vcs.conf.5: $(srcdir)/vcs.conf.man.xml |
$(DOCBOOK_TO_MAN) "$<" |
.PHONY: all clean lint xhtml |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/profiles/black.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: White-on-Black |
# $Id$ |
bg_contact=Black |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
fg_title=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/profiles/white.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Black-on-White profile |
# $Id$ |
bg_contact=White |
bg_heading=$bg_contact |
bg_title=$bg_contact |
bg_sign=$bg_contact |
fg_heading=Black |
fg_title=$fg_heading |
fg_sign=$fg_heading |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/profiles/compact.conf |
---|
0,0 → 1,11 |
# vcs:conf: |
# vcs:desc: Compact mosaic, 6x12 contact sheet (small) |
# $Id: compact.conf 2331 2011-08-30 02:50:59Z toni $ |
disable_shadows=1 |
disable_timestamps=1 |
padding=0 |
captures=72 |
height=40 |
timecode_from=$TC_NUMCAPS |
columns=12 |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/profiles/mosaic.conf |
---|
0,0 → 1,12 |
# vcs:conf: |
# vcs:desc: Tight, small, thumbnails |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id$ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
captures=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/common.mk |
---|
0,0 → 1,91 |
# $Id$ |
# |
# To be included from GNUmakefile or BSDmakefile |
# To use it directly set VERSION and PACKAGER |
# e.g. make VERSION=1.x PACKAGER=Me <rule> |
# |
# Notes to self: |
# This file should follow only common/portable make syntax and commands |
# Common pitfalls: |
# - $(shell) -> GNU Make, equivalent BSD make: != |
# - install -D -> GNU only (-d is portable) |
# - $(RM) -> empty by default in BSD, set from BSDmakefile |
prefix:=/usr/local |
DESTDIR:=/ |
TGZ=vcs-$(VERSION).tar.gz |
MANDIR:=$(prefix)/share/man |
all: docs/vcs.1 docs/vcs.conf.5 vcs.spec |
# |
# Automatically detected value: |
# PACKAGER=$(PACKAGER) |
# To set it manually add it to Make's command-line like: |
# $$ $(MAKE) PACKAGER="This Is My Name" |
dist: vcs-$(VERSION).tar.gz |
vcs-$(VERSION).tar.gz: all |
$(RM) -r vcs-$(VERSION) vcs-$(VERSION).tar.gz |
mkdir vcs-$(VERSION) |
tar c --exclude='.svn' \ |
--exclude='*.swp' --exclude='*.swo' \ |
--exclude='vcs-$(VERSION)' . |\ |
tar x -C vcs-$(VERSION) |
tar zcf vcs-$(VERSION).tar.gz vcs-$(VERSION)/ |
$(RM) -r vcs-$(VERSION) |
docs/vcs.1 docs/vcs.conf.5: |
$(GMAKE) -C docs `basename $@` |
# Files installed in packages |
prepackage: examples/vcs.conf.example |
install: |
install -d $(DESTDIR)$(prefix)/bin/ |
install -m755 vcs $(DESTDIR)$(prefix)/bin/vcs |
install -d $(DESTDIR)$(prefix)/share/vcs/profiles |
install -m644 profiles/*.conf $(DESTDIR)$(prefix)/share/vcs/profiles/ |
install -d $(DESTDIR)$(MANDIR)/man1/ $(DESTDIR)$(MANDIR)/man5/ |
install -m644 docs/vcs.1 $(DESTDIR)$(MANDIR)/man1/ |
install -m644 docs/vcs.conf.5 $(DESTDIR)$(MANDIR)/man5/ |
uninstall: |
$(RM) $(DESTDIR)$(prefix)/bin/vcs |
$(RM) $(DESTDIR)$(MANDIR)/man1/vcs.1 $(DESTDIR)$(MANDIR)/man5/vcs.conf.5 |
for file in profiles/*.conf ; do \ |
$(RM) $(DESTDIR)$(prefix)/share/vcs/profiles/`basename $$file` ; \ |
done |
-rmdir -p $(DESTDIR)$(prefix)/bin |
-rmdir -p $(DESTDIR)$(prefix)/share/vcs/profiles |
-rmdir -p $(DESTDIR)$(MANDIR)/man1 $(DESTDIR)$(MANDIR)/man5 |
examples/vcs.conf.example: docs/src/vcs.conf.example |
sed -e 's/^/#/;s/^#$$//;s/^##/#/' < $< > $@ |
vcs.spec: rpm/vcs.spec.in vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be defined |
@echo "[creating vcs.spec]" |
@cat $< | sed 's!@VERSION@!$(VERSION)!g' | \ |
sed 's!@PACKAGER@!$(PACKAGER)!g' > $@ |
# PKGBUILD CAN'T BE INCLUDED in the archive |
PKGBUILD: arch/PKGBUILD.in $(TGZ) vcs |
test "$(VERSION)" # Version (=$(VERSION)) must be detected |
@echo "[PKGBUILD]" |
@MD5=$(shell md5sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA1=$(shell sha1sum -b $(TGZ) | cut -d' ' -f1) ; \ |
SHA256=$(shell sha256sum -b $(TGZ) | cut -d' ' -f1) ; \ |
cat $< | sed -e 's!@VERSION@!$(VERSION)!g' \ |
-e "s/@MD5@/$$MD5/g" \ |
-e "s/@SHA1@/$$SHA1/g" -e "s/@SHA256@/$$SHA256/g" > $@ |
clean: |
#-$(RM) examples/vcs.conf.example |
$(MAKE) -C docs clean |
distclean: clean |
-$(RM) vcs.spec PKGBUILD vcs-$(VERSION).tar.gz |
.PHONY: all install clean tgz |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/examples/vcs.conf.example |
---|
0,0 → 1,159 |
# |
# vcs:conf: $Rev$ |
# Example vcs.conf file |
# This example files contains all the default values, commented out. |
# For each setting, where an equivalent command-line option exists it will be |
# listed in the comments |
# |
# Location: |
# Configuration files can be placed at /etc/vcs.conf (site-wide), |
# ~/.vcs.conf (current user, hidden file!), ./vcs.conf (current dir) or |
# ~/.vcs/vcs.conf (current user, new alternative location) |
# |
# Syntax: |
# * Comments start with '#' or ';' |
# * '#' can be used in values by writing $# |
# * Semicolons (;) can't be used in values |
# * Options are of the form name=value |
# - Options can refer to the *current* value of other options, i.e. |
# font_sign=$font_heading will assign to 'font_sign' the same value |
# as 'font_heading'. If font_heading is changed after that, font_sign |
# won't be affected. |
# - See <http://p.outlyer.net/dox/vcs:conf_files> for the full list |
# Height of individual captures. Percentage or fixed size (in pixels) |
#height=100% # option -H |
# Amount ignored from the end of the video (think of it as an anti-spoiler |
# measure). Percentage (of video duration) or time (e.g. 2m). Set to 0 |
# to disable it. |
#end_offset=5% # option -E |
######################## |
# Contact Sheet Layout # |
######################## |
#columns=2 # Number of columns in the contact sheet (option -c) |
#interval=1m # Interval between captures (option -i) |
# Number of captures. Note setting this isn't enough to use a fixed number, the |
# mode must also be changed (option 'timecode_from', see below). (option -n) |
#captures=16 |
# Mode of operation, can be either $TC_NUMCAPS or $TC_INTERVAL (the default) |
# $TC_NUMCAPS Fix the number of captures, adjust interval accordingly |
# $TC_INTERVAL Fix interval, adjust the number of captures accordingly |
# (options -i and -n set this implicitly) |
#timecode_from=$TC_INTERVAL |
# Modifier for extended mode (option -e). |
# Beware, setting it to something different from 0 automatically enables extended mode! |
#extended_factor=0 |
# Extra padding added around each capture. |
# This has *no effect* when shadows are enabled (the default). |
# Tweaking this might break alignment when using extended mode (-e) |
#padding=2 |
# Anonymous mode, set to 1 to disable the "Preview created by {value of user}" |
# line in the footer. (option --anonymous) |
#anonymous=0 |
# Profiles to load by default. (option -p) |
# *MUST* exist. |
#profiles= |
############### |
# Output file # |
############### |
# Output format. Use the file extension (e.g. 'png', 'jpg', 'jp2') |
# Any format accepted by ImageMagick can be used here (even pdf or gif, not |
# that they'll look very good though) |
# (options: -j for JPEG, -j2 for JPEG 2000) |
#format=png |
#quality=92 # Output quality for lossy formats (e.g. jpg) |
#################### |
# Cosmetic touches # |
#################### |
#user=`id -un` # User name, will be used in the contact sheet footer (option -u) |
# Used in the signature, e.g. "Preview created by {value of user}" |
#signature=Preview created by |
#disable_shadows=0 # Disable shadows by default (option -ds) |
#disable_timestamps=0 # Disable timestamps by default (option -dt) |
##################### |
# Colours and fonts # |
##################### |
# * Colours can be defined either by their name or through hexcodes |
# use the command 'identify -list color' for a list of known names |
# * Colours can use transparency, although this usually only makes sense |
# for timestamps, which are overlaid on captures. |
# * Beware when specifying colours in hex format color=#hex is ok |
# but color = #hex not, spaces can be used with color = $#hex |
# * Font sizes are expressed in points and hence might need adjustment |
# when the font is changed. |
# * Font names can be either font paths (e.g. /usr/something/ttf/font.ttf) |
# or font names as understood by ImageMagick, use the command |
# 'identify -list font' for a list of fonts known to ImageMagick |
#bg_heading=#afcd7a # Heading/meta-information section background colour |
#fg_heading=Black # Heading font colour |
#font_heading=DejaVu-Sans-Book # Heading font |
#pts_heading=14 # Font size for heading |
#bg_title=White # Background for the title (if activated with option -T) |
#fg_title=Black # Title font colour |
#font_title=$font_heading # Title font |
#bg_contact=White # Background for the contact sheet |
#bg_tstamps=#000000aa # Background for timestamps. Note the use of transparency |
#fg_tstamps=White # Timestamps font colour |
#font_tstamps=$font_heading # Timestamps font |
#pts_tstamps=14 # Font size for timestamps |
# Background for the signature, i.e. the section after the contact sheet with |
# vcs and user identification |
#bg_sign=SlateGray |
#fg_sign=Black # Font colour for the signature |
#font_sign=$font_heading # Font for the signature |
#pts_sign=10 # Font size for signature |
# Font to use for file name when the non-latin mode (--nonlatin) is enabled |
# Option: -Ik=fontname |
#nonlatin_font= # Autodetected (hopefully) |
###################### |
# Lower level tweaks # |
###################### |
# Decoder to use by default, can be either $DEC_FFMPEG (default) or |
# $DEC_MPLAYER |
# (options: -M for MPlayer, -F for FFMpeg) |
#decoder=$DEC_FFMPEG |
# Output from commands, useful to locate errors |
#stdout=/dev/null |
#stderr=/dev/null |
# Verbosity level, very verbose by default. |
# Possible values: $V_ALL (default), $V_ERROR, $V_WARN, $V_INFO, $V_NONE |
# (options: -q for quietness) |
#verbosity=$V_ALL |
# 1 disables colours in console output |
#simple_feedback=0 |
#debug=0 # When 1, enables debugging mode (option -D) |
#getopt=getopt # GNU Getopt executable name |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/examples/black-mosaic.conf |
---|
0,0 → 1,17 |
# vcs:profile: |
# vcs:desc: Tight sheet with white on black |
# <http://p.outlyer.net/dox/vcs:example_configs> |
# $Id: black-mosaic.conf 2323 2011-08-28 23:05:13Z toni $ |
disable_timestamps=1 |
disable_shadows=1 |
height=160 |
numcaps=20 |
timecode_from=$TC_NUMCAPS |
padding=0 |
columns=4 |
bg_contact=Black |
bg_heading=$bg_contact |
bg_sign=$bg_contact |
fg_heading=White |
fg_sign=$fg_heading |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/examples/black-compact-chain.conf |
---|
0,0 → 1,6 |
# vcs:profile: |
# vcs:desc: Compact mosaic (small) with white on black |
# Exampled of "chained" profiles, profiles loaded from other profiles |
# $Id: black-compact-chain.conf 2323 2011-08-28 23:05:13Z toni $ |
profiles=black,compact |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/dist/README |
---|
0,0 → 1,39 |
Index |
----- |
1. Files |
2. Installation |
3. Uninstallation |
Files |
----- |
In this package: |
vcs The VCS script |
profiles/ Example profiles: |
mosaic.conf 20 small thumbnails in a 5x4 grid, no padding |
black.conf Black background and white text |
white.conf White background and black text |
examples/vcs.conf Example configuration |
Use "make examples/vcs.conf.example" to create |
a version with all options commented out. |
Installation |
------------ |
$ make install |
Will install under /usr/local |
$ make install prefix=/usr |
Will install under /usr |
Uninstallation |
-------------- |
$ make uninstall |
If you used a prefix during install use it too during uninstall |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/Makefile |
---|
0,0 → 1,114 |
# |
# $Id$ |
# |
srcdir=dist |
#VER=$(shell grep VERSION= $(srcdir)/vcs | sed 's/.*"\([^"]*\)".*/\1/') |
VER=$(shell sed -n '/VERSION=/s/.*"\([^"]*\)".*/\1/p' $(srcdir)/vcs | head -n1) |
all: |
@echo "-------------------------------------------------------------------------------" |
@echo " Use: " |
@echo " $$ $(MAKE) dist # to create the actual v$(VER) distribution files" |
@echo " $$ $(MAKE) manpages # to create only the manpages (in $(srcdir)/docs)" |
@echo " $$ $(MAKE) docs # to create all documentation formats (in $(srcdir)/docs)" |
@echo |
@echo " $$ $(MAKE) lint # to validate documentation sources" |
@echo " $$ $(MAKE) clean # to clean generated files" |
@echo " $$ $(MAKE) distclean # to clean generated and distribution files" |
@echo " $$ $(MAKE) uploadclean # to clean non-distribution files" |
@echo "------------------------------------------------------------------------------" |
docs: lint |
$(MAKE) -C $(srcdir)/docs all |
manpages: lint |
$(MAKE) -C $(srcdir)/docs vcs.1 vcs.conf.5 |
lint: |
$(MAKE) -C $(srcdir)/docs lint |
tgz: vcs-$(VER).tar.gz |
vcs-$(VER).tar.gz: $(srcdir)/vcs-$(VER).tar.gz |
mv $< $@ |
$(srcdir)/vcs-$(VER).tar.gz: |
make -C $(srcdir) distclean `basename $@` |
check-no-svn: |
@if [ -d .svn ]; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo "** Don't release from SVN working copy **" ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
check-rel: |
@if head -n50 vcs | grep -q 'RELEASE=0' ; then \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo '** RELEASE is set to 0! **' ; \ |
echo '*************************************************' ; \ |
echo '*************************************************' ; \ |
echo ; \ |
fi |
dist: check-rel check-no-svn \ |
vcs-$(VER).tar.gz \ |
PKGBUILD-$(VER) \ |
$(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG \ |
rpm deb |
# This shouldn't be re-built |
devel_tools/mansrc/settings.man.inc.xml: |
cd `dirname $@` && $(MAKE) |
PKGBUILD-$(VER): vcs-$(VER).tar.gz |
cd $(srcdir) && ln -s ../vcs-$(VER).tar.gz ./ |
make -C $(srcdir) PKGBUILD |
$(RM) $(srcdir)/vcs-$(VER).tar.gz |
mv $(srcdir)/PKGBUILD $@ |
vcs-$(VER).gz: $(srcdir)/vcs |
gzip -c9 < vcs > $@ |
vcs-$(VER).bz2: $(srcdir)/vcs |
bzip2 -c9 < vcs > $@ |
vcs-$(VER).bash: $(srcdir)/vcs |
cat $< > $@ |
CHANGELOG.gz: $(srcdir)/CHANGELOG |
gzip -c9 < $< > $@ |
CHANGELOG: $(srcdir)/CHANGELOG |
cp $< $@ |
distclean: clean |
$(RM) PKGBUILD-$(VER) vcs-$(VER).tar.gz $(addprefix vcs-$(VER), .gz .bz2 .bash) \ |
CHANGELOG.gz CHANGELOG *.deb *.rpm |
# That's the old distclean |
uploadclean: |
$(RM) -ri vcs Makefile *.changes dist |
deb: |
cd dist && debuild -k0x5812006E -us -uc && debclean |
#$(RM) vcs_*.changes vcs_*.build |
rpm: vcs-$(VER).tar.gz |
rpmbuild --clean -tb vcs-$(VER).tar.gz |
test -d ~/rpmbuild/RPMS/noarch && ln -s ~/rpmbuild/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
test -d ~/RPM/RPMS/noarch && ln -s ~/RPM/RPMS/noarch/vcs-$(VER)-*.rpm . || true |
clean: |
-$(RM) vcs[-_]$(VER)* CHANGELOG* |
make -C $(srcdir)/docs clean |
.PHONY: all docs manpages lint clean dist distclean uploadclean \ |
check-no-svn check-rel \ |
deb rpm tgz |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/online_man/Makefile |
---|
0,0 → 1,20 |
# |
# $Id$ |
# |
docsdir=../dist/docs |
all: man.vcs.html man.vcs.conf.html |
man.vcs.html: $(docsdir)/vcs.man.xhtml |
cp $< $@ |
man.vcs.conf.html: $(docsdir)/vcs.conf.man.xhtml |
cp $< $@ |
$(docsdir)/%: |
make -C $(docsdir) $* |
clean: |
$(RM) man.vcs.html man.vcs.conf.html |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/online_man/man.css |
---|
0,0 → 1,36 |
/*$Rev: 2317 $*/ |
body { |
font-size-adjust:/*0.58*/0.5; |
font-size:12pt; |
background-color:#333; |
color:#eee; |
} |
a:link, a:active { color: #5692c4; } |
a:visited { color: #76b2e4; } |
a:hover { color: #ff6347; /*Tomato;*/ } |
.errorcode { font-family:monospace; } |
.warning, .note, .tip { |
margin-bottom:1ex; |
color:#333; |
} |
.note a:link, .note a:active, .note a:visited, |
.tip a:link, .tip a:active, .tip a:visited { |
color:navy; |
} |
.note a:hover, .tip a:hover { color: #800; } |
.warning { |
border:2px dashed #ffa500; |
background: #fc4 url("/usr/share/icons/gnome/48x48/status/dialog-warning.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.note, .tip { |
border:2px dashed navy; |
background: #69f url("/usr/share/icons/gnome/48x48/status/dialog-information.png") no-repeat 4px 12px; |
padding:0 1em 0 52px; |
} |
.programlisting { |
background:#555; |
padding:1ex; |
width:100ex; |
border:1px solid #222; |
} |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/online_man/.htaccess |
---|
0,0 → 1,2 |
IndexIgnore man.css |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/tests/GNUmakefile |
---|
0,0 → 1,38 |
# $Id$ |
VCS:=../vcs |
#VCS:=../portability/oldvcs/vcs-1.11.2 |
extract=sed -n "/^$*()"'/,/^}$$/p' "$(VCS)" |
TESTS_FILE=src/tests.txt |
TEST_MAKER=src/make_test.bash |
get_interval_reqs = $(addprefix inc/, \ |
$(addsuffix .func.bash,get_interval trace error \ |
is_number tolower assert awkexf fptest \ |
fsimeq notice) \ |
$(addsuffix .inc.bash,constants) \ |
) |
all: get_interval |
inc/constants.inc.bash: $(VCS) |
mkdir -p inc/ |
echo 'declare -r RELEASE=0' > $@ |
echo 'declare DEBUG=1' >> $@ |
echo 'INTERNAL_TRACE_FILTER=TRACE_NOTHING' >>$@ |
echo '$(shell grep -m1 'VERSION=' "$(VCS)")' >> $@ |
sed -n '/{{{ # Constants/,/}}}/p' "$(VCS)" >> $@ |
get_interval: $(TESTS_FILE) $(get_interval_reqs) |
$(TEST_MAKER) $@ $(get_interval_reqs) > $@.test.bash |
chmod +x $@.test.bash |
inc/%.func.bash: $(VCS) |
mkdir -p inc |
$(extract) >$@ |
clean: |
$(RM) inc/* *.test.bash |
-rmdir -p inc/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/tests/src/make_test.bash |
---|
0,0 → 1,30 |
#!/bin/bash |
# This file can be used to generate a test script |
# The actual tests are contained in tests.txt |
testsfile=$(dirname "$0")/tests.txt |
TESTNAME=$1 |
shift |
REQS=$@ |
echo '#!/bin/bash' |
for req in $REQS; do |
echo "source $req" |
done |
echo "source src/unittest.bash" |
echo 'while read line ; do' |
echo ' unittest $line' |
echo 'done <<< "$(sed "/^[[:space:]]*#/d" "'$testsfile'" | grep "^'${TESTNAME}' ")"' |
echo 'if [[ $RET -eq 0 ]]; then' |
echo ' echo -n "${G}All tests passed"' |
echo 'else' |
echo ' echo -n "${R}Some tests failed"' |
echo 'fi' |
echo 'echo $CLR' |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/tests/src/unittest.bash |
---|
0,0 → 1,47 |
# |
# $Id$ |
# Receives the raw input as found in tests.txt |
# |
TESTNUM=0 |
G=$(tput setaf 2 ; tput bold ) |
R=$(tput setaf 1 ; tput bold) |
CLR=$(tput sgr0) |
RET=0 |
function unittest { |
let 'TESTNUM++' |
a="$@" |
fn=$(cut -d' ' -f1 <<<"$a") |
if [[ $TESTNUM -eq 1 ]]; then |
type $fn |
fi |
args=$(cut -d' ' -f2- <<<"$a" | sed 's/:.*$//' | sed 's/ *$//') |
expected=$(cut -d' ' -f2- <<<"$a" | sed 's/.*://') |
echo "$fn($args) -> $expected" >&2 |
res=$($fn $args) |
ret=$? |
passed= |
if [[ $expected == '><' ]]; then # Expected to fail |
if [[ $ret != 0 ]]; then |
passed=1 |
else |
passed=0 |
fi |
elif [[ $res != $expected ]] && ( [[ $res ]] && ! fptest "$res" ~ "$expected" ) ; then |
passed=0 |
else |
passed=1 |
fi |
if [[ $passed -ne 1 ]]; then |
echo -n "${R}FAILED => $res != '$expected'" |
let 'RET++' |
else |
echo -n "${G}PASSED => $res ~= $expected" |
fi |
echo $CLR |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/tests/src/tests.txt |
---|
0,0 → 1,41 |
# $Id$ |
# Format: |
# test input [input ...] : expected_result |
# >< as expected result means the operation will fail |
#################### |
#################### get_interval() tests |
#################### |
get_interval 1h : 3600 |
get_interval 1h1m : 3660 |
get_interval 1h1m1 : 3661 |
get_interval 1h1m1s : 3661 |
get_interval 100 : 100 |
# Leading 0's |
get_interval 010 : 10 |
get_interval 01h0m01m01s : 3661 |
# Case insensitive |
get_interval 1H1M1S1s : 3662 |
# Reverse order of mangnitudes |
get_interval 1s1m1h : 3661 |
get_interval 1.22 : 1.22 |
get_interval 1s.22 : 1.22 |
get_interval .11.11.11 : 0.33 |
get_interval 1s.11.11 : 1.22 |
# Rejected inputs |
get_interval s : >< |
get_interval .11s : >< |
get_interval 1ss : >< |
# Repeated units |
get_interval 1s1s1s1s : 4 |
get_interval 1m1m1m1m : 240 |
get_interval 1h1h1h1h : 14400 |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/tests/test_funkymodes |
---|
0,0 → 1,27 |
#!/usr/bin/env bash |
# Allow setting from the environment |
[ "$vcs" ] || vcs='vcs' |
if [ -z "$1" ]; then |
echo "Usage: $0 <file>" |
exit 1 |
fi >&2 |
HEIGHT="-H240" |
echo "Using vcs: $vcs" >&2 |
yes 01234 | head -n200 > randsource |
BN=$(basename "$1") |
echo ">> Standard <<" >&2 |
$vcs -n4 -c2 $HEIGHT "$1" -o "$BN-std.jpg" |
echo ">> Polaroid <<" >&2 |
$vcs -n6 -c3 -k polaroid $HEIGHT "$1" -R randsource -o "$BN-polaroid.jpg" |
echo ">> Photos <<" >&2 |
$vcs -n6 -c3 -k photos $HEIGHT "$1" -R randsource -o "$BN-photos.jpg" |
echo ">> Filmstrip <<" >&2 |
$vcs -n8 -c2 -k film $HEIGTH "$1" -R randsource -o "$BN-film.jpg" |
rm -f randsource |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color/vcs |
---|
0,0 → 1,0 |
link dist/vcs |
Property changes: |
Added: svn:special |
+* |
\ No newline at end of property |
/ATTIC/video-contact-sheet/branches/1.13.2-pre.3+early_color |
---|
Property changes: |
Added: svn:mergeinfo |
Merged /video-contact-sheet/branches/1.0a:r262-263 |
Merged /video-contact-sheet/tags/1.11:r381,385-387 |
Merged /video-contact-sheet/tags/1.12:r413 |
Merged /video-contact-sheet/branches/1.0.100a:r364-371 |
Merged /video-contact-sheet/tags/1.12.3:r456-457 |
Merged /video-contact-sheet/tags/1.0.12:r352-356 |
Merged /video-contact-sheet/tags/1.0.11:r344-345 |
Merged /video-contact-sheet/tags/1.0.2b:r274 |
Merged /video-contact-sheet/tags/1.0.8a:r319-320 |
Merged /video-contact-sheet/branches/1.11:r375-379,382-383 |
Merged /video-contact-sheet/branches/1.12:r409-411 |
Merged /video-contact-sheet/branches/1.13:r460-564 |
Merged /video-contact-sheet/branches/1.0.1a:r266-267 |
Merged /video-contact-sheet/branches/1.0.12:r347-350 |
Merged /video-contact-sheet/branches/1.0.11:r334-342 |
Merged /video-contact-sheet/branches/1.0.10:r328-331 |
Merged /video-contact-sheet/branches/1.11.1:r389-390 |
Merged /video-contact-sheet/branches/1.11.2:r393-406 |
Merged /video-contact-sheet/branches/1.12.1:r416-419 |
Merged /video-contact-sheet/tags/0.99a:r261 |
Merged /video-contact-sheet/branches/1.13.1:r567-571 |
Merged /video-contact-sheet/branches/1.12.3:r435-454 |
Merged /video-contact-sheet/branches/1.12.2:r422-431 |
Merged /video-contact-sheet/branches/1.0.3b:r276-277 |
Merged /video-contact-sheet/branches/1.0.2b:r270-271 |
Merged /video-contact-sheet/branches/1.0.5b:r284-285 |
Merged /video-contact-sheet/branches/1.0.4b:r280-281 |
Merged /video-contact-sheet/branches/1.0.7a:r294-311 |
Merged /video-contact-sheet/branches/1.0.6b:r289-290 |
Merged /video-contact-sheet/branches/1.0.9a:r322-325 |
Merged /video-contact-sheet/branches/1.0.99:r358-361 |
Merged /video-contact-sheet/branches/1.0.8a:r315-317 |