mirror of
https://github.com/amiaopensource/ffmprovisr.git
synced 2024-12-25 03:08:20 +01:00
2231 lines
171 KiB
HTML
2231 lines
171 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<title>ffmprovisr</title>
|
||
<meta name="viewport" charset="utf-8" content="text/html, width=device-width, initial-scale=1">
|
||
<link href="https://fonts.googleapis.com/css?family=Montserrat%7CMerriweather" rel="stylesheet" type="text/css">
|
||
<link rel="stylesheet" href="css/css.css">
|
||
<link rel="icon" href="img/vhs.ico">
|
||
<script src="js/jquery.min.js"></script>
|
||
<script src="js/js.js"></script>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="grid">
|
||
<div class="header">
|
||
<h1>➺ ffmprovisr ❥</h1>
|
||
</div>
|
||
|
||
<nav class="sidebar well">
|
||
<h2 class="heading">Table of Contents</h2>
|
||
<a href="#about"><div class="contents-list">About this resource</div></a>
|
||
<a href="#basics"><div class="contents-list">FFmpeg basics</div></a>
|
||
<a href="#concepts"><div class="contents-list">Advanced FFmpeg concepts</div></a>
|
||
<a href="#rewrap"><div class="contents-list">Change container (rewrap)</div></a>
|
||
<a href="#transcode"><div class="contents-list">Change codec (transcode)</div></a>
|
||
<a href="#video-properties"><div class="contents-list">Change video properties</div></a>
|
||
<a href="#audio-files"><div class="contents-list">Change/view audio properties</div></a>
|
||
<a href="#join-trim"><div class="contents-list">Join/trim/create an excerpt</div></a>
|
||
<a href="#interlacing"><div class="contents-list">Work with interlaced video</div></a>
|
||
<a href="#overlay"><div class="contents-list">Overlay timecode or text on a video</div></a>
|
||
<a href="#create-images"><div class="contents-list">Create thumbnails or GIFs</div></a>
|
||
<a href="#create-video"><div class="contents-list">Create a video from image(s) and audio</div></a>
|
||
<a href="#filters-scopes"><div class="contents-list">Use filters or scopes</div></a>
|
||
<a href="#metadata"><div class="contents-list">View or strip metadata</div></a>
|
||
<a href="#preservation"><div class="contents-list">Preservation tasks</div></a>
|
||
<a href="#test-files"><div class="contents-list">Generate test files</div></a>
|
||
<a href="#ocr"><div class="contents-list">Use OCR</div></a>
|
||
<a href="#perceptual-similarity"><div class="contents-list">Compare similarity of videos</div></a>
|
||
<a href="#other"><div class="contents-list">Something else</div></a>
|
||
</nav>
|
||
|
||
<div class="content">
|
||
<div class="well">
|
||
<h2 class="heading" id="about">About ffmprovisr</h2>
|
||
<span class="intro-lead">Making FFmpeg Easier</span>
|
||
<p>FFmpeg is a powerful tool for manipulating audiovisual files. Unfortunately, it also has a steep learning curve, especially for users unfamiliar with a command line interface. This app helps users through the command generation process so that more people can reap the benefits of FFmpeg.</p>
|
||
<p>Each button displays helpful information about how to perform a wide variety of tasks using FFmpeg. To use this site, click on the task you would like to perform. A new window will open up with a sample command and a description of how that command works. You can copy this command and understand how the command works with a breakdown of each of the flags.</p>
|
||
<span class="intro-lead">Tutorials</span>
|
||
<p>For FFmpeg basics, check out the program’s <a href="https://www.ffmpeg.org/" target="_blank">official website</a>.</p>
|
||
<p>For instructions on how to install FFmpeg on Mac, Linux, and Windows, refer to Reto Kromer’s <a href="https://avpres.net/FFmpeg/#ch1" target="_blank">installation instructions</a>.</p>
|
||
<p>For Bash and command line basics, try the <a href="https://learnpythonthehardway.org/book/appendixa.html" target="_blank">Command Line Crash Course</a>. For a little more context presented in an ffmprovisr style, try <a href="http://explainshell.com/" target="_blank">explainshell.com</a>!</p>
|
||
<span class="intro-lead">License</span>
|
||
<p class="license">
|
||
<a href="https://creativecommons.org/licenses/by/4.0/" target="_blank"><img alt="Creative Commons License" src="img/cc.png"></a><br>
|
||
This work is licensed under a <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank">Creative Commons Attribution 4.0 International License</a>.
|
||
</p>
|
||
<span class="intro-lead">Sister projects</span>
|
||
<p><a href="http://dd388.github.io/crals/" target="_blank">Script Ahoy</a>: Community Resource for Archivists and Librarians Scripting</p>
|
||
<p><a href="https://datapraxis.github.io/sourcecaster/" target="_blank">The Sourcecaster</a>: an app that helps you use the command line to work through common challenges that come up when working with digital primary sources.</p>
|
||
<p><a href="https://amiaopensource.github.io/cable-bible/" target="_blank">Cable Bible</a>: A Guide to Cables and Connectors Used for Audiovisual Tech</p>
|
||
</div>
|
||
|
||
<div class="well">
|
||
<h2 id="basics">Learn about FFmpeg basics</h2>
|
||
<!-- Basic structure of an FFmpeg command -->
|
||
<label class="recipe" for="basic-structure">Basic structure of an FFmpeg command</label>
|
||
<input type="checkbox" id="basic-structure">
|
||
<div class="hiding">
|
||
<h3>Basic structure of an FFmpeg command</h3>
|
||
<p>At its basis, an FFmpeg command is relatively simple. After you have installed FFmpeg (see instructions <a href="https://avpres.net/FFmpeg/#ch1" target="_blank">here</a>), the program is invoked simply by typing <code>ffmpeg</code> at the command prompt.</p>
|
||
<p>Subsequently, each instruction that you supply to FFmpeg is actually a pair: a flag, which designates the <i>type</i> of action you want to carry out; and then the specifics of that action. Flags are always prepended with a hyphen.</p>
|
||
<p>For example, in the instruction <code>-i <i>input_file.ext</i></code>, the <code>-i</code> flag tells FFmpeg that you are supplying an input file, and <code>input_file.ext</code> states which file it is.</p>
|
||
<p>Likewise, in the instruction <code>-c:v prores</code>, the flag <code>-c:v</code> tells FFmpeg that you want to encode the video stream, and <code>prores</code> specifies which codec is to be used. (<code>-c:v</code> is shorthand for <code>-codec:v</code>/<code>-codec:video</code>).</p>
|
||
<p>A very basic FFmpeg command looks like this:</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file.ext</i></dt><dd>path and name of the input file<br></dd>
|
||
<dt><i>-flag some_action</i></dt><dd>tell FFmpeg to do something, by supplying a valid flag and action</dd>
|
||
<dt><i>output_file.ext</i></dt><dd>path and name of the output file.<br>
|
||
Because this is the last part of the command, the filename you type here does not have a flag designating it as the output file.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- End Basic structure of an FFmpeg command -->
|
||
|
||
<!-- Streaming vs. Saving -->
|
||
<label class="recipe" for="streaming-saving">Streaming vs. Saving</label>
|
||
<input type="checkbox" id="streaming-saving">
|
||
<div class="hiding">
|
||
<h3>Streaming vs. Saving</h3>
|
||
<p>FFplay allows you to stream created video and FFmpeg allows you to save video.</p>
|
||
<p>The following command creates and saves a 10-second video of SMPTE bars:</p>
|
||
<code>ffmpeg -f lavfi -i smptebars=size=640x480 -t 5 output_file</code>
|
||
<p>This command plays and streams SMPTE bars but does not save them on the computer:</p>
|
||
<code>ffplay -f lavfi smptebars=size=640x480</code>
|
||
<p>The main difference is small but significant: the <code>-i</code> flag is required for FFmpeg but not required for FFplay. Additionally, the FFmpeg script needs to have <code>-t 5</code> and <code>output.mkv</code> added to specify the length of time to record and the place to save the video.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- End Streaming vs. Saving -->
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="concepts">Learn about more advanced FFmpeg concepts</h2>
|
||
|
||
<!-- Filtergraph explanation -->
|
||
<label class="recipe" for="filtergraphs">Filtergraphs</label>
|
||
<input type="checkbox" id="filtergraphs">
|
||
<div class="hiding">
|
||
<h3>Filtergraphs</h3>
|
||
<p>Many FFmpeg commands use filters that manipulate the video or audio stream in some way: for example, <a href="http://ffmpeg.org/ffmpeg-filters.html#hflip" target="_blank">hflip</a> to horizontally flip a video, or <a href="http://ffmpeg.org/ffmpeg-filters.html#amerge-1" target="_blank">amerge</a> to merge two or more audio tracks into a single stream.</p>
|
||
<p>The use of a filter is signalled by the flag <code>-vf</code> (video filter) or <code>-af</code> (audio filter), followed by the name and options of the filter itself. For example, take the <a href="#convert-colourspace">convert colourspace</a> command:</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf colormatrix=<i>src</i>:<i>dst</i> <i>output_file</i></code>
|
||
<p>Here, <a href="http://ffmpeg.org/ffmpeg-filters.html#colormatrix" target="_blank">colormatrix</a> is the filter used, with <i>src</i> and <i>dst</i> representing the source and destination colourspaces. This part following the <code>-vf</code> is a <b>filtergraph</b>.</p>
|
||
<p>It is also possible to apply multiple filters to an input, which are sequenced together in the filtergraph. A chained set of filters is called a filter chain, and a filtergraph may include multiple filter chains. Filters in a filterchain are separated from each other by commas (<code>,</code>), and filterchains are separated from each other by semicolons (<code>;</code>). For example, take the <a href="#inverse-telecine">inverse telecine</a> command:</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf "fieldmatch,yadif,decimate" <i>output_file</i></code></p>
|
||
<p>Here we have a filtergraph including one filter chain, which is made up of three video filters.</p>
|
||
<p>It is often prudent to enclose your filtergraph in quotation marks; this means that you can use spaces within the filtergraph. Using the inverse telecine example again, the following filter commands are all valid and equivalent:
|
||
<ul>
|
||
<li><code>-vf fieldmatch,yadif,decimate</code></li>
|
||
<li><code>-vf "fieldmatch,yadif,decimate"</code></li>
|
||
<li><code>-vf "fieldmatch, yadif, decimate"</code></li>
|
||
</ul>
|
||
but <code>-vf fieldmatch, yadif, decimate</code> is not valid.</p>
|
||
<p>The ordering of the filters is significant. Video filters are applied in the order given, with the output of one filter being passed along as the input to the next filter in the chain. In the example above, <code>fieldmatch</code> reconstructs the original frames from the inverse telecined video, <code>yadif</code> deinterlaces (this is a failsafe in case any combed frames remain, for example if the source mixes telecined and real interlaced content), and <code>decimate</code> deletes duplicated frames. Clearly, it is not possible to delete duplicated frames before those frames are reconstructed.</p>
|
||
<h4>Notes</h4>
|
||
<ul>
|
||
<li>If the command involves more than one input or output, you must use the flag <code>-filter_complex</code> instead of <code>-vf</code>.</li>
|
||
<li>Straight quotation marks ("like this") rather than curved quotation marks (“like this”) should be used.</li>
|
||
</ul>
|
||
<p>For more information, check out the FFmpeg wiki <a href="https://trac.ffmpeg.org/wiki/FilteringGuide" target="_blank">Filtering Guide</a>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- End Filtergraph explanation -->
|
||
|
||
<!-- Stream mapping explanation -->
|
||
<label class="recipe" for="stream-mapping">Stream mapping</label>
|
||
<input type="checkbox" id="stream-mapping">
|
||
<div class="hiding">
|
||
<h3>Stream mapping</h3>
|
||
<p>Stream mapping is the practice of defining which of the streams (e.g., video or audio tracks) present in an input file will be present in the output file. FFmpeg recognises five stream types:</p>
|
||
<ul>
|
||
<li><code>a</code> - audio</li>
|
||
<li><code>v</code> - video</li>
|
||
<li><code>s</code> - subtitle</li>
|
||
<li><code>d</code> - data (including timecode tracks)</li>
|
||
<li><code>t</code> - attachment</li>
|
||
</ul>
|
||
<p>Mapping is achieved by use of the <code>-map</code> flag, followed by an action of the type <code>file_number:stream_type[:stream_number]</code>. Numbering is zero-indexed, and it's possible to map by stream type and/or overall stream order within the input file. For example:</p>
|
||
<ul>
|
||
<li><code>-map 0:v</code> means ‘take all video streams from the first input file’.</li>
|
||
<li><code>-map 0:3</code> means ‘take the fourth stream from the first input file’.</li>
|
||
<li><code>-map 0:a:2</code> means ‘take the third audio stream from the first input file’.</li>
|
||
<li><code>-map 0:0 -map 0:2</code> means ‘take the first and third streams from the first input file’.</li>
|
||
<li><code>-map 0:1 -map 1:0</code> means ‘take the second stream from the first input file and the first stream from the second input file’.</li>
|
||
</ul>
|
||
<p>To map <i>all</i> streams in the input file to the output file, use <code>-map 0</code>. However, note that not all container formats can include all stream types: for example, .mp4 cannot contain timecode.</p>
|
||
<p>When no mapping is specified in an ffmpeg command, the default for video files is to take just one video and one audio stream for the output: other stream types, such as timecode or subtitles, will not be copied to the output file by default. If multiple video or audio streams are present, the best quality one is automatically selected by FFmpeg.</p>
|
||
<p>For more information, check out the FFmpeg wiki <a href="https://trac.ffmpeg.org/wiki/Map" target="_blank">Map</a> page, and the official FFmpeg <a href="http://ffmpeg.org/ffmpeg.html#Advanced-options" target="_blank">documentation on <code>-map</code></a>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- End Stream Mapping explanation -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="rewrap">Change container (rewrap)</h2>
|
||
|
||
<!-- Basic rewrap command -->
|
||
<label class="recipe" for="basic-rewrap">Basic rewrap command</label>
|
||
<input type="checkbox" id="basic-rewrap">
|
||
<div class="hiding">
|
||
<h3>Rewrap a file</h3>
|
||
<p><code>ffmpeg -i <i>input_file.ext</i> -c copy -map 0 <i>output_file.ext</i></code></p>
|
||
<p>This script will rewrap a video file. It will create a new video video file where the inner content (the video, audio, and subtitle data) of the original file is unchanged, but these streams are rehoused within a different container format.</p>
|
||
<p><b>Note:</b> rewrapping is also known as remuxing, short for re-multiplexing.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file.ext</i></dt><dd>path and name of the input file<br></dd>
|
||
<dt>-c copy</dt><dd>copy the streams directly, without re-encoding.</dd>
|
||
<dt>-map 0</dt><dd>map all streams of the input to the output.<br>
|
||
By default, FFmpeg will only map one stream of each type (video, audio, subtitles) to the output file. However, files may have multiple streams of a given type - for example, a video may have several audio tracks for different languages. Therefore, if you want to preserve all the streams in the original, it's necessary to use this option.</dd>
|
||
<dt><i>output_file.ext</i></dt><dd>path and name of the output file.<br>
|
||
The new container you are rewrapping to is defined by the filename extension used here, e.g. .mkv, .mp4, .mov.</dd>
|
||
</dl>
|
||
<h4>Important caveat</h4>
|
||
<p>It may not be possible to rewrap a file's contents to a new container without re-encoding one or more of the streams within (that is, the video, audio, and subtitle tracks). Some containers can only contain streams of a certain encoding type: for example, the .mp4 container does not support uncompressed audio tracks. (In practice .mp4 goes hand-in-hand with a H.264-encoded video stream and an AAC-encoded video stream, although other types of video and audio streams are possible). Another example is that the Matroska container does not allow data tracks; see the <a href="#mkv-to-mp4">MKV to MP4 recipe</a>.</p>
|
||
<p>In such cases, FFmpeg will throw an error. If you encounter errors of this kind, you may wish to consult the <a href="#transcode">list of transcoding recipes</a>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- End Basic rewrap command -->
|
||
|
||
<!-- MKV to MP4 -->
|
||
<label class="recipe" for="mkv_to_mp4">Convert Matroska (MKV) to MP4</label>
|
||
<input type="checkbox" id="mkv_to_mp4">
|
||
<div class="hiding">
|
||
<h3>MKV to MP4</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i>.mkv -c:v copy -c:a aac <i>output_file</i>.mp4</code></p>
|
||
<p>This will convert your Matroska (MKV) files to MP4 files.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path and name of the input file<br>
|
||
The extension for the Matroska container is <code>.mkv</code>.</dd>
|
||
<dt>-c:v copy</dt><dd>copies the video stream without re-encoding it</dd>
|
||
<dt>-c:a aac</dt><dd>re-encodes the audio stream using the AAC audio codec<br>
|
||
Note that sadly MP4 cannot contain sound encoded by a PCM (Pulse-Code Modulation) audio codec.<br>
|
||
For silent videos you can replace <code>-c:a aac</code> by <code>-an</code>, which means that there will be no audio track in the output file.</dd>
|
||
<dt><i>output_file</i></dt><dd>path and name of the output file<br>
|
||
The extension for the MP4 container is <code>.mp4</code>.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends MKV to MP4 -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="transcode">Change codec (transcode)</h2>
|
||
|
||
<!-- Transcode to ProRes -->
|
||
<label class="recipe" for="to_prores">Transcode to deinterlaced Apple ProRes LT</label>
|
||
<input type="checkbox" id="to_prores">
|
||
<div class="hiding">
|
||
<h3>Transcode into a deinterlaced Apple ProRes LT</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v prores -profile:v 1 -vf yadif -c:a pcm_s16le <i>output_file</i>.mov</code></p>
|
||
<p>This command transcodes an input file into a deinterlaced Apple ProRes 422 LT file with 16-bit linear PCM encoded audio. The file is deinterlaced using the yadif filter (Yet Another De-Interlacing Filter).</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v prores</dt><dd>tells FFmpeg to transcode the video stream into Apple ProRes 422</dd>
|
||
<dt>-profile:v <i>1</i></dt><dd>Declares profile of ProRes you want to use. The profiles are explained below:
|
||
<ul>
|
||
<li>0 = ProRes 422 (Proxy)</li>
|
||
<li>1 = ProRes 422 (LT)</li>
|
||
<li>2 = ProRes 422 (Standard)</li>
|
||
<li>3 = ProRes 422 (HQ)</li>
|
||
</ul></dd>
|
||
<dt>-vf yadif</dt><dd>Runs a deinterlacing video filter (yet another deinterlacing filter) on the new file. <code>-vf</code> is an alias for <code>-filter:v</code>.</dd>
|
||
<dt>-c:a pcm_s16le</dt><dd>tells FFmpeg to encode the audio stream in 16-bit linear PCM</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file<br>
|
||
The extension for the QuickTime container is <code>.mov</code>.</dd>
|
||
</dl>
|
||
<p>FFmpeg comes with more than one ProRes encoder:</p>
|
||
<ul>
|
||
<li><code>prores</code> is much faster, can be used for progressive video only, and seems to be better for video according to Rec. 601 (Recommendation ITU-R BT.601).</li>
|
||
<li><code>prores_ks</code> generates a better file, can also be used for interlaced video, allows also encoding of ProRes 4444 (<code>-c:v prores_ks -profile:v 4</code>), and seems to be better for video according to Rec. 709 (Recommendation ITU-R BT.709).</li>
|
||
</ul>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Transcode to ProRes -->
|
||
|
||
<!-- Transcode to H.264 -->
|
||
<label class="recipe" for="transcode_h264">Transcode to an H.264 access file</label>
|
||
<input type="checkbox" id="transcode_h264">
|
||
<div class="hiding">
|
||
<h3>Transcode to H.264</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -pix_fmt yuv420p -c:a copy <i>output_file</i></code></p>
|
||
<p>This command takes an input file and transcodes it to H.264 with an .mp4 wrapper, keeping the audio the same codec as the original. The libx264 codec defaults to a “medium” preset for compression quality and a CRF of 23. CRF stands for constant rate factor and determines the quality and file size of the resulting H.264 video. A low CRF means high quality and large file size; a high CRF means the opposite.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx264</dt><dd>tells FFmpeg to encode the video stream as H.264</dd>
|
||
<dt>-pix_fmt yuv420p</dt><dd>libx264 will use a chroma subsampling scheme that is the closest match to that of the input. This can result in Y′C<sub>B</sub>C<sub>R</sub> 4:2:0, 4:2:2, or 4:4:4 chroma subsampling. QuickTime and most other non-FFmpeg based players can’t decode H.264 files that are not 4:2:0. In order to allow the video to play in all players, you can specify 4:2:0 chroma subsampling.</dd>
|
||
<dt>-c:a copy</dt><dd>tells FFmpeg to copy the audio stream without re-encoding it</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>In order to use the same basic command to make a higher quality file, you can add some of these presets:</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -pix_fmt yuv420p -preset veryslow -crf 18 -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>-preset <i>veryslow</i></dt><dd>This option tells FFmpeg to use the slowest preset possible for the best compression quality.<br>
|
||
Available presets, from slowest to fastest, are: <code>veryslow</code>, <code>slower</code>, <code>slow</code>, <code>medium</code>, <code>fast</code>, <code>faster</code>, <code>veryfast</code>, <code>superfast</code>, <code>ultrafast</code>.</dd>
|
||
<dt>-crf <i>18</i></dt><dd>Specifying a lower CRF will make a larger file with better visual quality. For H.264 files being encoded with a 4:2:0 chroma subsampling scheme (i.e., using <code>-pix_fmt yuv420p</code>), the scale ranges between 0-51, with 0 being lossless and 51 the worst possible quality.<br>
|
||
If no crf is specified, <code>libx264</code> will use a default value of 23. 18 is often considered a “visually lossless” compression.</dd>
|
||
</dl>
|
||
<p>For more information, see the <a href="https://trac.ffmpeg.org/wiki/Encode/H.264" target="_blank">FFmpeg and H.264 Encoding Guide</a> on the FFmpeg wiki.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Transcode to H.264 -->
|
||
|
||
<!-- H.264 from DCP -->
|
||
<label class="recipe" for="dcp_to_h264">Transcode from DCP to an H.264 access file</label>
|
||
<input type="checkbox" id="dcp_to_h264">
|
||
<div class="hiding">
|
||
<h3>H.264 from DCP</h3>
|
||
<p><code>ffmpeg -i <i>input_video_file</i>.mxf -i <i>input_audio_file</i>.mxf -c:v libx264 -pix_fmt yuv420p -c:a aac <i>output_file.mp4</i></code></p>
|
||
<p>This will transcode MXF wrapped video and audio files to an H.264 encoded MP4 file. Please note this only works for unencrypted, single reel DCPs.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_video_file</i></dt><dd>path and name of the video input file. This extension must be <code>.mxf</code></dd>
|
||
<dt>-i <i>input_audio_file</i></dt><dd>path and name of the audio input file. This extension must be <code>.mxf</code></dd>
|
||
<dt>-c:v libx264</dt><dd>transcodes video to H.264</dd>
|
||
<dt>-pix_fmt yuv420p</dt><dd>sets pixel format to yuv420p for greater compatibility with media players</dd>
|
||
<dt>-c:a aac</dt><dd>re-encodes using the AAC audio codec<br>
|
||
Note that sadly MP4 cannot contain sound encoded by a PCM (Pulse-Code Modulation) audio codec</dd>
|
||
<dt><i>output_file.mp4</i></dt><dd>path, name and .mp4 extension of the output file</dd>
|
||
</dl>
|
||
<p>Variation: Copy PCM audio streams by using Matroska instead of the MP4 container</p>
|
||
<p><code>ffmpeg -i <i>input_video_file</i>.mxf -i <i>input_audio_file</i>.mxf -c:v libx264 -pix_fmt yuv420p -c:a copy <i>output_file.mkv</i></code></p>
|
||
<dl>
|
||
<dt>-c:a copy</dt><dd>re-encodes using the same audio codec</dd>
|
||
<dt><i>output_file.mkv</i></dt><dd>path, name and .mkv extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends H.264 from DCP -->
|
||
|
||
<!-- Transcode to FFV1.mkv -->
|
||
<label class="recipe" for="create_FFV1_mkv">Transcode your file with the FFV1 Version 3 Codec in a Matroska container</label>
|
||
<input type="checkbox" id="create_FFV1_mkv">
|
||
<div class="hiding">
|
||
<h3>Create FFV1 Version 3 video in a Matroska container with framemd5 of input</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -map 0 -dn -c:v ffv1 -level 3 -g 1 -slicecrc 1 -slices 16 -c:a copy <i>output_file</i>.mkv -f framemd5 -an <i>framemd5_output_file</i></code></p>
|
||
<p>This will losslessly transcode your video with the FFV1 Version 3 codec in a Matroska container. In order to verify losslessness, a framemd5 of the source video is also generated. For more information on FFV1 encoding, <a href="https://trac.ffmpeg.org/wiki/Encode/FFV1" target="_blank">try the FFmpeg wiki</a>.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command.</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file.</dd>
|
||
<dt>-map 0</dt><dd>Map all streams that are present in the input file. This is important as FFmpeg will map only one stream of each type (video, audio, subtitles) by default to the output video.</dd>
|
||
<dt>-dn</dt><dd>ignore data streams (data no). The Matroska container does not allow data tracks.</dd>
|
||
<dt>-c:v ffv1</dt><dd>specifies the FFV1 video codec.</dd>
|
||
<dt>-level 3</dt><dd>specifies Version 3 of the FFV1 codec.</dd>
|
||
<dt>-g 1</dt><dd>specifies intra-frame encoding, or GOP=1.</dd>
|
||
<dt>-slicecrc 1</dt><dd>Adds CRC information for each slice. This makes it possible for a decoder to detect errors in the bitstream, rather than blindly decoding a broken slice. (Read more <a href="http://ndsr.nycdigital.org/diving-in-head-first/" target="_blank">here</a>).</dd>
|
||
<dt>-slices 16</dt><dd>Each frame is split into 16 slices. 16 is a good trade-off between filesize and encoding time.</dd>
|
||
<dt>-c:a copy</dt><dd>copies all mapped audio streams.</dd>
|
||
<dt><i>output_file</i>.mkv</dt><dd>path and name of the output file. Use the <code>.mkv</code> extension to save your file in a Matroska container. Optionally, choose a different extension if you want a different container, such as <code>.mov</code> or <code>.avi</code>.</dd>
|
||
<dt>-f framemd5</dt><dd>Decodes video with the framemd5 muxer in order to generate MD5 checksums for every frame of your input file. This allows you to verify losslessness when compared against the framemd5s of the output file.</dd>
|
||
<dt>-an</dt><dd>ignores the audio stream when creating framemd5 (audio no)</dd>
|
||
<dt><i>framemd5_output_file</i></dt><dd>path, name and extension of the framemd5 file.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Transcode to FFV1.mkv-->
|
||
|
||
<!-- Rip DVD -->
|
||
<label class="recipe" for="dvd_to_file">Convert DVD to H.264</label>
|
||
<input type="checkbox" id="dvd_to_file">
|
||
<div class="hiding">
|
||
<h3>Convert DVD to H.264</h3>
|
||
<p><code>ffmpeg -i concat:<i>input_file1</i>\|<i>input_file2</i>\|<i>input_file3</i> -c:v libx264 -c:a copy <i>output_file</i>.mp4</code></p>
|
||
<p>This command allows you to create an H.264 file from a DVD source that is not copy-protected.</p>
|
||
<p>Before encoding, you’ll need to establish which of the .VOB files on the DVD or .iso contain the content that you wish to encode. Inside the VIDEO_TS directory, you will see a series of files with names like VTS_01_0.VOB, VTS_01_1.VOB, etc. Some of the .VOB files will contain menus, special features, etc, so locate the ones that contain target content by playing them back in VLC.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i concat:<i>input files</i></dt><dd>lists the input VOB files and directs FFmpeg to concatenate them. Each input file should be separated by a backslash and a pipe, like so:<br>
|
||
<code>-i concat:VTS_01_1.VOB\|VTS_01_2.VOB\|VTS_01_3.VOB</code><br>
|
||
The backslash is simply an escape character for the pipe (<b>|</b>).</dd>
|
||
<dt>-c:v libx264</dt><dd>sets the video codec as H.264</dd>
|
||
<dt>-c:a copy</dt><dd>audio remains as-is (no re-encode)</dd>
|
||
<dt><i>output_file.mp4</i></dt><dd>path and name of the output file</dd>
|
||
</dl>
|
||
<p>It’s also possible to adjust the quality of your output by setting the <b>-crf</b> and <b>-preset</b> values:</p>
|
||
<p><code>ffmpeg -i concat:<i>input_file1</i>\|<i>input_file2</i>\|<i>input_file3</i> -c:v libx264 -crf 18 -preset veryslow -c:a copy <i>output_file</i>.mp4</code></p>
|
||
<dl>
|
||
<dt>-crf 18</dt><dd>sets the constant rate factor to a visually lossless value. Libx264 defaults to a <a href="https://trac.ffmpeg.org/wiki/Encode/H.264#crf" target="_blank">crf of 23</a>, considered medium quality; a smaller CRF value produces a larger and higher quality video.</dd>
|
||
<dt>-preset veryslow</dt><dd>A slower preset will result in better compression and therefore a higher-quality file. The default is <b>medium</b>; slower presets are <b>slow</b>, <b>slower</b>, and <b>veryslow</b>.</dd>
|
||
</dl>
|
||
<p>Bear in mind that by default, libx264 will only encode a single video stream and a single audio stream, picking the ‘best’ of the options available. To preserve all video and audio streams, add <b>-map</b> parameters:</p>
|
||
<p><code>ffmpeg -i concat:<i>input_file1</i>\|<i>input_file2</i> -map 0:v -map 0:a -c:v libx264 -c:a copy <i>output_file</i>.mp4</code></p>
|
||
<dl>
|
||
<dt>-map 0:v</dt><dd>encodes all video streams</dd>
|
||
<dt>-map 0:a</dt><dd>encodes all audio streams</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends rip DVD -->
|
||
|
||
<!-- Transcode to H.265 -->
|
||
<label class="recipe" for="transcode_h265">Transcode to an H.265/HEVC MP4</label>
|
||
<input type="checkbox" id="transcode_h265">
|
||
<div class="hiding">
|
||
<h3>Transcode to H.265/HEVC</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx265 -pix_fmt yuv420p -c:a copy <i>output_file</i></code></p>
|
||
<p>This command takes an input file and transcodes it to H.265/HEVC in an .mp4 wrapper, keeping the audio codec the same as in the original file.</p>
|
||
<p><b>Note:</b> FFmpeg must be compiled with libx265, the library of the H.265 codec, for this script to work. (Add the flag <code>--with-x265</code> if using the <code>brew install ffmpeg</code> method).</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx265</dt><dd>tells FFmpeg to encode the video as H.265</dd>
|
||
<dt>-pix_fmt yuv420p</dt><dd>libx265 will use a chroma subsampling scheme that is the closest match to that of the input. This can result in Y′C<sub>B</sub>C<sub>R</sub> 4:2:0, 4:2:2, or 4:4:4 chroma subsampling. For widest accessibility, it’s a good idea to specify 4:2:0 chroma subsampling.</dd>
|
||
<dt>-c:a copy</dt><dd>tells FFmpeg not to change the audio codec</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>The libx265 encoding library defaults to a ‘medium’ preset for compression quality and a CRF of 28. CRF stands for ‘constant rate factor’ and determines the quality and file size of the resulting H.265 video. The CRF scale ranges from 0 (best quality [lossless]; largest file size) to 51 (worst quality; smallest file size).</p>
|
||
<p>A CRF of 28 for H.265 can be considered a medium setting, <a href="https://trac.ffmpeg.org/wiki/Encode/H.265#ConstantRateFactorCRF" target="_blank">corresponding</a> to a CRF of 23 in <a href="./index.html#transcode_h264">encoding H.264</a>, but should result in about half the file size.</p>
|
||
<p>To create a higher quality file, you can add these presets:</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx265 -pix_fmt yuv420p -preset veryslow -crf 18 -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>-preset <i>veryslow</i></dt><dd>This option tells FFmpeg to use the slowest preset possible for the best compression quality.</dd>
|
||
<dt>-crf <i>18</i></dt><dd>Specifying a lower CRF will make a larger file with better visual quality. 18 is often considered a ‘visually lossless’ compression.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Transcode to H.265 -->
|
||
|
||
<p> </p>
|
||
<!-- Here comes audio-only transcoding -->
|
||
|
||
<!-- WAV to MP3 -->
|
||
<label class="recipe" for="wav_to_mp3">Convert WAV to MP3</label>
|
||
<input type="checkbox" id="wav_to_mp3">
|
||
<div class="hiding">
|
||
<h3>WAV to MP3</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i>.wav -write_id3v1 1 -id3v2_version 3 -dither_method rectangular -out_sample_rate 48k -qscale:a 1 <i>output_file</i>.mp3</code></p>
|
||
<p>This will convert your WAV files to MP3s.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path and name of the input file</dd>
|
||
<dt>-write_id3v1 1</dt><dd>This will write metadata to an ID3v1 tag at the head of the file, assuming you’ve embedded metadata into the WAV file.</dd>
|
||
<dt>-id3v2_version 3</dt><dd>This will write metadata to an ID3v2.3 tag at the tail of the file, assuming you’ve embedded metadata into the WAV file.</dd>
|
||
<dt>-dither_method rectangular</dt><dd>Dither makes sure you don’t unnecessarily truncate the dynamic range of your audio.</dd>
|
||
<dt>-out_sample_rate 48k</dt><dd>Sets the audio sampling frequency to 48 kHz. This can be omitted to use the same sampling frequency as the input.</dd>
|
||
<dt>-qscale:a 1</dt><dd>This sets the encoder to use a constant quality with a variable bitrate of between 190-250kbit/s. If you would prefer to use a constant bitrate, this could be replaced with <code>-b:a 320k</code> to set to the maximum bitrate allowed by the MP3 format. For more detailed discussion on variable vs constant bitrates see <a href="https://trac.ffmpeg.org/wiki/Encode/MP3" target="_blank">here.</a></dd>
|
||
<dt><i>output_file</i></dt><dd>path and name of the output file</dd>
|
||
</dl>
|
||
<p>A couple notes</p>
|
||
<ul>
|
||
<li>About ID3v2.3 tag: ID3v2.3 is better supported than ID3v2.4, FFmpeg's default ID3v2 setting.</li>
|
||
<li>About dither methods: FFmpeg comes with a variety of dither algorithms, outlined in the <a href="https://ffmpeg.org/ffmpeg-resampler.html" target="_blank">official docs</a>, though some may lead to unintended, drastic digital clipping on some systems.</li>
|
||
</ul>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends WAV to MP3 -->
|
||
|
||
<!-- append notice to access mp3 -->
|
||
<label class="recipe" for="append_mp3">Generate two access MP3s (with and without copyright).</label>
|
||
<input type="checkbox" id="append_mp3">
|
||
<div class="hiding">
|
||
<h3>Generate two access MP3s from input. One with appended audio (such as a copyright notice) and one unmodified.</h3>
|
||
<p> <code>ffmpeg -i <i>input_file</i> -i <i>input_file_to_append</i> -filter_complex "[0:a:0]asplit=2[a][b];[b]afifo[bb];[1:a:0][bb]concat=n=2:v=0:a=1[concatout]" -map "[a]" -codec:a libmp3lame -dither_method modified_e_weighted -qscale:a 2 <i>output_file.mp3</i> -map "[concatout]" -codec:a libmp3lame -dither_method modified_e_weighted -qscale:a 2 <i>output_file_appended.mp3</i></code></p>
|
||
<p>This script allows you to generate two derivative audio files from a master while appending audio from a separate file (for example a copyright or institutional notice) to one of them.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file (the master file)</dd>
|
||
<dt>-i <i>input_file_to_append</i></dt><dd>path, name and extension of the input file (the file to be appended to access file)</dd>
|
||
<dt>-filter_complex</dt><dd>enables the complex filtering to manage splitting the input to two audio streams</dd>
|
||
<dt>[0:a:0]asplit=2[a][b];</dt><dd><code>asplit</code> allows audio streams to be split up for separate manipulation. This command splits the audio from the first input (the master file) into two streams "a" and "b"</dd>
|
||
<dt>[b]afifo[bb];</dt><dd>this buffers the stream "b" to help prevent dropped samples and renames stream to "bb"</dd>
|
||
<dt>[1:a:0][bb]concat=n=2:v=0:a=1[concatout]</dt><dd><code>concat</code> is used to join files. <code>n=2</code> tells the filter there are two inputs. <code>v=0:a=1</code> Tells the filter there are 0 video outputs and 1 audio output. This command appends the audio from the second input to the beginning of stream "bb" and names the output "concatout"</dd>
|
||
<dt>-map "[a]"</dt><dd>this maps the unmodified audio stream to the first output</dd>
|
||
<dt>-codec:a libmp3lame -dither_method modified_e_weighted -qscale:a 2</dt><dd>sets up MP3 options (using constant quality)</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file (unmodified)</dd>
|
||
<dt>-map "[concatout]"</dt><dd>this maps the modified stream to the second output</dd>
|
||
<dt>-codec:a libmp3lame -dither_method modified_e_weighted -qscale:a 2</dt><dd>sets up MP3 options (using constant quality)</dd>
|
||
<dt><i>output_file_appended</i></dt><dd>path, name and extension of the output file (with appended notice)</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends append notice to access mp3 -->
|
||
|
||
<!-- WAV to AAC/MP4 -->
|
||
<label class="recipe" for="wav_to_mp4">Convert WAV to AAC/MP4</label>
|
||
<input type="checkbox" id="wav_to_mp4">
|
||
<div class="hiding">
|
||
<h3>WAV to AAC/MP4</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i>.wav -c:a aac -b:a 128k -dither_method rectangular -ar 44100 <i>output_file</i>.mp4</code></p>
|
||
<p>This will convert your WAV file to AAC/MP4.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path and name of the input file</dd>
|
||
<dt>-c:a aac</dt><dd>sets the audio codec to AAC</dd>
|
||
<dt>-b:a 128k</dt><dd>sets the bitrate of the audio to 128k</dd>
|
||
<dt>-dither_method rectangular</dt><dd>Dither makes sure you don’t unnecessarily truncate the dynamic range of your audio.</dd>
|
||
<dt>-ar 44100</dt><dd>sets the audio sampling frequency to 44100 Hz, or 44.1 kHz, or “CD quality”</dd>
|
||
<dt><i>output_file</i></dt><dd>path and name of the output file</dd>
|
||
</dl>
|
||
<p>A note about dither methods. FFmpeg comes with a variety of dither algorithms, outlined in the <a href="https://ffmpeg.org/ffmpeg-resampler.html" target="_blank">official docs</a>, though some may lead to unintended, not-subtle digital clipping on some systems.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends WAV to AAC/MP4 -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="video-properties">Change video properties</h2>
|
||
|
||
<!-- 4:3 to 16:9 -->
|
||
<label class="recipe" for="SD_HD">Transform 4:3 aspect ratio into 16:9 with pillarbox</label>
|
||
<input type="checkbox" id="SD_HD">
|
||
<div class="hiding">
|
||
<h3>Transform 4:3 aspect ratio into 16:9 with pillarbox</h3>
|
||
<p>Transform a video file with 4:3 aspect ratio into a video file with 16:9 aspect ratio by correct pillarboxing.</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter:v "pad=ih*16/9:ih:(ow-iw)/2:(oh-ih)/2" -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v "pad=ih*16/9:ih:(ow-iw)/2:(oh-ih)/2"</dt><dd>video padding<br>This resolution independent formula is actually padding any aspect ratio into 16:9 by pillarboxing, because the video filter uses relative values for input width (iw), input height (ih), output width (ow) and output height (oh).</dd>
|
||
<dt>-c:a copy</dt><dd>re-encodes using the same audio codec<br>
|
||
For silent videos you can replace <code>-c:a copy</code> by <code>-an</code>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends 4:3 to 16:9 -->
|
||
|
||
<!-- 16:9 to 4:3 -->
|
||
<label class="recipe" for="HD_SD">Transform 16:9 aspect ratio video into 4:3 with letterbox</label>
|
||
<input type="checkbox" id="HD_SD">
|
||
<div class="hiding">
|
||
<h3>Transform 16:9 aspect ratio video into 4:3 with letterbox</h3>
|
||
<p>Transform a video file with 16:9 aspect ratio into a video file with 4:3 aspect ratio by correct letterboxing.</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter:v "pad=iw:iw*3/4:(ow-iw)/2:(oh-ih)/2" -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v "pad=iw:iw*3/4:(ow-iw)/2:(oh-ih)/2"</dt><dd>video padding<br>
|
||
This resolution independent formula is actually padding any aspect ratio into 4:3 by letterboxing, because the video filter uses relative values for input width (iw), input height (ih), output width (ow) and output height (oh).</dd>
|
||
<dt>-c:a copy</dt><dd>re-encodes using the same audio codec<br>
|
||
For silent videos you can replace <code>-c:a copy</code> by <code>-an</code>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends 16:9 to 4:3 -->
|
||
|
||
<!-- Flip image -->
|
||
<label class="recipe" for="flip_image">Flip video image</label>
|
||
<input type="checkbox" id="flip_image">
|
||
<div class="hiding">
|
||
<h3>Flip the video image horizontally and/or vertically</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter:v "hflip,vflip" -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v "hflip,vflip"</dt><dd>flips the image horizontally and vertically<br>By using only one of the parameters hflip or vflip for filtering the image is flipped on that axis only. The quote marks are not mandatory.</dd>
|
||
<dt>-c:a copy</dt><dd>re-encodes using the same audio codec<br>
|
||
For silent videos you can replace <code>-c:a copy</code> by <code>-an</code>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Flip image -->
|
||
|
||
<!-- SD to HD -->
|
||
<label class="recipe" for="SD_HD_2">Transform SD to HD with pillarbox</label>
|
||
<input type="checkbox" id="SD_HD_2">
|
||
<div class="hiding">
|
||
<h3>Transform SD into HD with pillarbox</h3>
|
||
<p>Transform a SD video file with 4:3 aspect ratio into an HD video file with 16:9 aspect ratio by correct pillarboxing.</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter:v "colormatrix=bt601:bt709, scale=1440:1080:flags=lanczos, pad=1920:1080:240:0" -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v "colormatrix=bt601:bt709, scale=1440:1080:flags=lanczos, pad=1920:1080:240:0"</dt><dd>set colour matrix, video scaling and padding<br>Three filters are applied:
|
||
<ol>
|
||
<li>The luma coefficients are modified from SD video (according to Rec. 601) to HD video (according to Rec. 709) by a colour matrix. Note that today Rec. 709 is often used also for SD and therefore you may cancel this parameter.</li>
|
||
<li>The scaling filter (<code>scale=1440:1080</code>) works for both upscaling and downscaling. We use the Lanczos scaling algorithm (<code>flags=lanczos</code>), which is slower but gives better results than the default bilinear algorithm.</li>
|
||
<li>The padding filter (<code>pad=1920:1080:240:0</code>) completes the transformation from SD to HD.</li>
|
||
</ol></dd>
|
||
<dt>-c:a copy</dt><dd>re-encodes using the same audio codec<br>
|
||
For silent videos you can replace <code>-c:a copy</code> with <code>-an</code>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends SD to HD -->
|
||
|
||
<!-- Change display aspect ratio without re-encoding video-->
|
||
<label class="recipe" for="change_DAR">Change display aspect ratio without re-encoding</label>
|
||
<input type="checkbox" id="change_DAR">
|
||
<div class="hiding">
|
||
<h3>Change Display Aspect Ratio without reencoding video</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v copy -aspect 4:3 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v copy</dt><dd>Copy all mapped video streams.</dd>
|
||
<dt>-aspect 4:3</dt><dd>Change Display Aspect Ratio to <code>4:3</code>. Experiment with other aspect ratios such as <code>16:9</code>. If used together with <code>-c:v copy</code>, it will affect the aspect ratio stored at container level, but not the aspect ratio stored in encoded frames, if it exists.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Change display aspect ratio without re-encoding video -->
|
||
|
||
<!-- Convert colourspace -->
|
||
<label class="recipe" for="convert-colourspace">Convert colourspace of video</label>
|
||
<input type="checkbox" id="convert-colourspace">
|
||
<div class="hiding">
|
||
<h3>Transcode video to a different colourspace</h3>
|
||
<p>This command uses a filter to convert the video to a different colour space.</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf colormatrix=src:dst <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx264</dt><dd>tells FFmpeg to encode the video stream as H.264</dd>
|
||
<dt>-vf colormatrix=<i>src</i>:<i>dst</i></dt><dd>the video filter <b>colormatrix</b> will be applied, with the given source and destination colourspaces.<br>
|
||
Accepted values include <code>bt601</code> (Rec.601), <code>smpte170m</code> (Rec.601, 525-line/<a href="https://en.wikipedia.org/wiki/NTSC#NTSC-M" target="_blank">NTSC</a> version), <code>bt470bg</code> (Rec.601, 625-line/<a href="https://en.wikipedia.org/wiki/PAL#PAL-B.2FG.2FD.2FK.2FI" target="_blank">PAL</a> version), <code>bt709</code> (Rec.709), and <code>bt2020</code> (Rec.2020).<br>
|
||
For example, to convert from Rec.601 to Rec.709, you would use <code>-vf colormatrix=bt601:bt709</code>.</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p><b>Note:</b> Converting between colourspaces with FFmpeg can be done via either the <b>colormatrix</b> or <b>colorspace</b> filters, with colorspace allowing finer control (individual setting of colourspace, transfer characteristics, primaries, range, pixel format, etc). See <a href="https://trac.ffmpeg.org/wiki/colorspace" target="_blank">this</a> entry on the FFmpeg wiki, and the FFmpeg documentation for <a href="http://ffmpeg.org/ffmpeg-filters.html#colormatrix" target="_blank">colormatrix</a> and <a href="http://ffmpeg.org/ffmpeg-filters.html#colorspace" target="_blank">colorspace</a>.</p>
|
||
<hr>
|
||
<h4>Convert colourspace and embed colourspace metadata</h4>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf colormatrix=src:dst -color_primaries <i>val</i> -color_trc <i>val</i> -colorspace <i>val</i> <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx264</dt><dd>encode video as H.264</dd>
|
||
<dt>-vf colormatrix=<i>src</i>:<i>dst</i></dt><dd>the video filter <b>colormatrix</b> will be applied, with the given source and destination colourspaces.</dd>
|
||
<dt>-color_primaries <i>val</i></dt><dd>tags video with the given colour primaries.<br>
|
||
Accepted values include <code>smpte170m</code> (Rec.601, 525-line/NTSC version), <code>bt470bg</code> (Rec.601, 625-line/PAL version), <code>bt709</code> (Rec.709), and <code>bt2020</code> (Rec.2020).
|
||
<dt>-color_trc <i>val</i></dt><dd>tags video with the given transfer characteristics (gamma).<br>
|
||
Accepted values include <code>smpte170m</code> (Rec.601, 525-line/NTSC version), <code>gamma28</code> (Rec.601, 625-line/PAL version)<sup><a href="#fn1" id="ref1">1</a></sup>, <code>bt709</code> (Rec.709), <code>bt2020_10</code> (Rec.2020 10-bit), and <code>bt2020_12</code> (Rec.2020 12-bit).</dd>
|
||
<dt>-colorspace <i>val</i></dt><dd>tags video as being in the given colourspace.<br>
|
||
Accepted values include <code>smpte170m</code> (Rec.601, 525-line/NTSC version), <code>bt470bg</code> (Rec.601, 625-line/PAL version), <code>bt709</code> (Rec.709), <code>bt2020_cl</code> (Rec.2020 constant luminance), and <code>bt2020_ncl</code> (Rec.2020 non-constant luminance).</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<h4>Examples</h4>
|
||
<p>To Rec.601 (525-line/NTSC):</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf colormatrix=bt709:smpte170m -color_primaries smpte170m -color_trc smpte170m -colorspace smpte170m <i>output_file</i></code></p>
|
||
<p>To Rec.601 (625-line/PAL):</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf colormatrix=bt709:bt470bg -color_primaries bt470bg -color_trc gamma28 -colorspace bt470bg <i>output_file</i></code></p>
|
||
<p>To Rec.709:</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf colormatrix=bt601:bt709 -color_primaries bt709 -color_trc bt709 -colorspace bt709 <i>output_file</i></code></p>
|
||
<p>MediaInfo output examples:</p>
|
||
<img src="./img/colourspace_metadata_mediainfo.png" alt="MediaInfo screenshots of colourspace metadata"><br>
|
||
<p><span class="beware">⚠</span> Using this command it is possible to add Rec.709 tags to a file that is actually Rec.601 (etc), so apply with caution!</p>
|
||
<p>These commands are relevant for H.264 and H.265 videos, encoded with <code>libx264</code> and <code>libx265</code> respectively.</p>
|
||
<p><b>Note:</b> If you wish to embed colourspace metadata <i>without</i> changing to another colourspace, omit <code>-vf colormatrix=src:dst</code>. However, since it is <code>libx264</code>/<code>libx265</code> that writes the metadata, it’s not possible to add these tags without reencoding the video stream.</p>
|
||
<p>For all possible values for <code>-color_primaries</code>, <code>-color_trc</code>, and <code>-colorspace</code>, see the FFmpeg documentation on <a href="https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options" target="_blank">codec options</a>.</p>
|
||
<hr>
|
||
<p id="fn1" class="footnote">1. Out of step with the regular pattern, <code>-color_trc</code> doesn’t accept <code>bt470bg</code>; it is instead here referred to directly as gamma.<br>
|
||
In the Rec.601 standard, 525-line/NTSC and 625-line/PAL video have assumed gammas of 2.2 and 2.8 respectively. <a href="#ref1" title="Jump back.">↩</a></p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Convert colourspace -->
|
||
|
||
<!-- Modify speed -->
|
||
<label class="recipe" for="modify_speed">Modify image and sound speed</label>
|
||
<input type="checkbox" id="modify_speed">
|
||
<div class="hiding">
|
||
<h3>Modify image and sound speed</h3>
|
||
<p>E.g. for converting 24fps to 25fps with audio pitch compensation for PAL access copies. (Thanks @kieranjol!)</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter_complex "[0:v]setpts=<i>input_fps</i>/<i>output_fps</i>*PTS[v]; [0:a]atempo=<i>output_fps</i>/<i>input_fps</i>[a]" -map "[v]" -map "[a]" <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter_complex "[0:v]setpts=<i>input_fps</i>/<i>output_fps</i>*PTS[v]; [0:a]atempo=<i>output_fps</i>/<i>input_fps</i>[a]"</dt><dd>A complex filter is needed here, in order to handle video stream and the audio stream separately. The <code>setpts</code> video filter modifies the PTS (presentation time stamp) of the video stream, and the <code>atempo</code> audio filter modifies the speed of the audio stream while keeping the same sound pitch. Note that the parameter order for the image and for the sound are inverted:
|
||
<ul>
|
||
<li>In the video filter <code>setpts</code> the numerator <code>input_fps</code> sets the input speed and the denominator <code>output_fps</code> sets the output speed; both values are given in frames per second.</li>
|
||
<li>In the sound filter <code>atempo</code> the numerator <code>output_fps</code> sets the output speed and the denominator <code>input_fps</code> sets the input speed; both values are given in frames per second.</li>
|
||
</ul>
|
||
The different filters in a complex filter can be divided either by comma or semicolon. The quotation marks allow to insert a space between the filters for readability.</dd>
|
||
<dt>-map "[v]"</dt><dd>maps the video stream and:</dd>
|
||
<dt>-map "[a]"</dt><dd>maps the audio stream together into:</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Modify speed -->
|
||
|
||
<!-- Make stream properties explicate -->
|
||
<label class="recipe" for="clarify_stream">Clarify stream properties</label>
|
||
<input type="checkbox" id="clarify_stream">
|
||
<div class="hiding">
|
||
<h3>Set stream properties</h3>
|
||
<h2>Find undetermined or unknown stream properties</h2>
|
||
<p>These examples use QuickTime inputs and outputs. The strategy will vary or may not be possible in other file formats. In the case of these examples it is the intention to make a lossless copy while clarifying an unknown characteristic of the stream.</p>
|
||
<p><code>ffprobe <i>input_file</i> -show_streams</code></p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-show_streams</dt><dd>Shows metadata of stream properties</dd>
|
||
</dl>
|
||
<p>Values that are set to 'unknown' and 'undetermined' may be unspecified within the stream. An unknown aspect ratio would be expressed as '0:1'. Streams with many unknown properties may have interoperability issues or not play as intended. In many cases, an unknown or undetermined value may be accurate because the information about the source is unclear, but often the value is intended to be known. In many cases the stream will played with an assumed value if undetermined (for instance a display_aspect_ratio of '0:1' may be played as 'WIDTH:HEIGHT'), but this may or may not be what is intended. Use carefully.</p>
|
||
<h2>Set aspect ratio</h2>
|
||
<p>If the display_aspect_ratio is set to '0:1' it may be clarified with the <i>-aspect</i> option and stream copy.</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c copy -map 0 -aspect DAR_NUM:DAR_DEN <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c copy</dt><dd>Usings stream copy for all streams</dd>
|
||
<dt>-map 0</dt><dd>tells FFmpeg to map all streams of the input to the output.</dd>
|
||
<dt>-aspect DAR_NUM:DAR_DEN</dt><dd>Replace DAR_NUM with the display aspect ratio numerator and DAR_DEN with the display aspect ratio denominator, such as <i>-aspect 4:3</i> or <i>-aspect 16:9</i>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<h2>Adding other stream properties.</h2>
|
||
<p>Other properties may be clarified in a similar way. Replace <i>-aspect</i> and its value with other properties such as shown in the options below. Note that setting color values in QuickTime requires that <i>-movflags write_colr</i> is set.</p>
|
||
<dl>
|
||
<dt>-color_primaries <i>VALUE</i> -movflags write_colr</dt><dd>Set a new color_primaries value.</dd>
|
||
<dt>-color_trc <i>VALUE</i> -movflags write_colr</dt><dd>Set a new color_transfer value.</dd>
|
||
<dt>-field_order <i>VALUE</i></dt><dd>Set interlacement values.</dd>
|
||
</dl>
|
||
<p>The possible values for <code>-color_primaries</code>, <code>-color_trc</code>, and <code>-field_order</code> are given in the <a href="http://ffmpeg.org/ffmpeg-all.html#toc-Codec-Options" target="_blank">Codec Options</a> section of the FFmpeg docs - scroll down to near the bottom of the section.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Make stream properties explicate -->
|
||
|
||
<!-- Crop video -->
|
||
<label class="recipe" for="crop_video">Crop video</label>
|
||
<input type="checkbox" id="crop_video">
|
||
<div class="hiding">
|
||
<h3>Crop video</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -vf "crop=<i>width</i>:<i>height</i>" <i>output_file</i></code></p>
|
||
<p>This command crops the input video to the dimensions defined</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-vf "<i>width</i>:<i>height</i>"</dt><dd>Crops the video to the given width and height (in pixels).<br>
|
||
By default, the crop area is centred: that is, the position of the top left of the cropped area is set to x = (<i>input_width</i> - <i>output_width</i>) / 2, y = <i>input_height</i> - <i>output_height</i>) / 2.
|
||
</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>It's also possible to specify the crop position by adding the x and y coordinates representing the top left of your cropped area to your crop filter, as such:</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -vf "crop=<i>width</i>:<i>height</i>[:<i>x_position</i>:<i>y_position</i>]" <i>output_file</i></code></p>
|
||
<h3>Examples</h3>
|
||
<p>The original frame, a screenshot of the SMPTE colourbars:</p>
|
||
<img class="sample-image" src="img/crop_example_orig.png" alt="VLC screenshot of Maggie Cheung">
|
||
<p>Result of the command <code>ffmpeg -i <i>smpte_coloursbars.mov</i> -vf "crop=500:500" <i>output_file</i></code>:</p>
|
||
<img class="sample-image-small" src="img/crop_example_aftercrop1.png" alt="VLC screenshot of Maggie Cheung, cropped from original">
|
||
<p>Result of the command <code>ffmpeg -i <i>smpte_coloursbars.mov</i> -vf "crop=500:500:0:0" <i>output_file</i></code>, appending <code>:0:0</code> to crop from the top left corner:</p>
|
||
<img class="sample-image-small" src="img/crop_example_aftercrop2.png" alt="VLC screenshot of Maggie Cheung, cropped from original">
|
||
<p>Result of the command <code>ffmpeg -i <i>smpte_coloursbars.mov</i> -vf "crop=500:300:500:30" <i>output_file</i></code>:</p>
|
||
<img class="sample-image-small" src="img/crop_example_aftercrop3.png" alt="VLC screenshot of Maggie Cheung, cropped from original">
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Crop video -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="audio-files">Change or view audio properties</h2>
|
||
|
||
<!-- Extract audio from an AV file -->
|
||
<label class="recipe" for="extract_audio">Extract audio without loss from an AV file</label>
|
||
<input type="checkbox" id="extract_audio">
|
||
<div class="hiding">
|
||
<h3>Extract audio from an AV file</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:a copy -vn <i>output_file</i></code></p>
|
||
<p>This command extracts the audio stream without loss from an audiovisual file.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:a copy</dt><dd>re-encodes using the same audio codec</dd>
|
||
<dt>-vn</dt><dd>no video stream</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Extract audio from am AV file -->
|
||
|
||
<!-- Combine audio tracks -->
|
||
<label class="recipe" for="combine_audio">Combine audio tracks</label>
|
||
<input type="checkbox" id="combine_audio">
|
||
<div class="hiding">
|
||
<h3>Combine audio tracks into one in a video file</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter_complex "[0:a:0][0:a:1]amerge[out]" -map 0:v -map "[out]" -c:v copy -shortest <i>output_file</i></code></p>
|
||
<p>This command combines two audio tracks present in a video file into one stream. It can be useful in situations where a downstream process, like YouTube’s automatic captioning, expect one audio track. To ensure that you’re mapping the right audio tracks run ffprobe before writing the script to identify which tracks are desired. More than two audio streams can be combined by extending the pattern present in the -filter_complex option.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter_complex</dt><dd>tells fmpeg that we will be using a complex filter</dd>
|
||
<dt>"</dt><dd>quotation mark to start filtergraph</dd>
|
||
<dt>[0:a:0][0:a:1]amerge[out]</dt><dd>combines the two audio tracks into one</dd>
|
||
<dt>"</dt><dd>quotation mark to end filtergraph</dd>
|
||
<dt>-map 0:v</dt><dd>map the video</dd>
|
||
<dt>-map "[out]"</dt><dd>map the combined audio defined by the filter</dd>
|
||
<dt>-c:v copy</dt><dd>copy the video</dd>
|
||
<dt>-shortest</dt><dd>limit to the shortest stream</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the video output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Combine audio tracks -->
|
||
|
||
<!-- phase shift -->
|
||
<label class="recipe" for="phase_shift">Inverses the audio phase of the second channel</label>
|
||
<input type="checkbox" id="phase_shift">
|
||
<div class="hiding">
|
||
<h3>Flip audio phase shift</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -af pan="stereo|c0=c0|c1=-1*c1" <i>output_file</i></code></p>
|
||
<p>This command inverses the audio phase of the second channel by rotating it 180°.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-af</dt><dd>specifies that the next section should be interpreted as an audio filter</dd>
|
||
<dt>pan=</dt><dd>tell the quoted text below to use the <a href="https://ffmpeg.org/ffmpeg-filters.html#pan-1" target="_blank">pan filter</a></dd>
|
||
<dt>"stereo|c0=c0|c1=-1*c1"</dt><dd>maps the output's first channel (c0) to the input's first channel and the output's second channel (c1) to the inverse of the input's second channel</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends phase shift -->
|
||
|
||
<!-- loudnorm metadata -->
|
||
<label class="recipe" for="loudnorm_metadata">Calculate Loudness Levels</label>
|
||
<input type="checkbox" id="loudnorm_metadata">
|
||
<div class="hiding">
|
||
<h3>Calculate Loudness Levels</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -af loudnorm=print_format=json -f null -</code></p>
|
||
<p>This filter calculates and outputs loudness information in json about an input file (labeled input) as well as what the levels would be if loudnorm were applied in its one pass mode (labeled output). The values generated can be used as inputs for a 'second pass' of the loudnorm filter allowing more accurate loudness normalization than if it is used in a single pass.</p>
|
||
<p>These instructions use the loudnorm defaults, which align well with PBS recommendations for target loudness. More information can be found at the <a href="https://ffmpeg.org/ffmpeg-filters.html#loudnorm" target="_blank">loudnorm documentation</a>.</p>
|
||
<p>Information about PBS loudness standards can be found in the <a href="http://www-tc.pbs.org/capt/Producing/TOS-2012-Pt2-Distribution.pdf" target="_blank">PBS Technical Operating Specifications</a> document. Information about EBU loudness standards can be found in the <a href="https://tech.ebu.ch/docs/r/r128-2014.pdf" target="_blank">EBU R 128</a> recommendation document.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-af loudnorm</dt><dd>activates the loudnorm filter</dd>
|
||
<dt>print_format=json</dt><dd>sets the output format for loudness information to json. This format makes it easy to use in a second pass. For a more human readable output, this can be set to <code>print_format=summary</code></dd>
|
||
<dt><i>-f null -</i></dt><dd>sets the file output to null (since we are only interested in the metadata generated)</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends loudnorm metadata -->
|
||
|
||
<!-- RIAA equalization -->
|
||
<label class="recipe" for="riaa_eq">RIAA Equalization</label>
|
||
<input type="checkbox" id="riaa_eq">
|
||
<div class="hiding">
|
||
<h3>RIAA Equalization</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -af aemphasis=type=riaa <i>output_file</i></code></p>
|
||
<p>This will apply RIAA equalization to an input file allowing correct listening of audio transferred 'flat' (without EQ) from records that used this EQ curve. For more information about RIAA equalization see the <a href="https://en.wikipedia.org/wiki/RIAA_equalization" target="_blank">Wikipedia page</a> on the subject.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-af aemphasis=type=riaa</dt><dd>activates the aemphasis filter and sets it to use RIAA equalization</dd>
|
||
<dt><i>output_file</i></dt><dd>path and name of output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends RIAA equalization -->
|
||
|
||
<!-- one pass loudnorm -->
|
||
<label class="recipe" for="loudnorm_one_pass">One Pass Loudness Normalization</label>
|
||
<input type="checkbox" id="loudnorm_one_pass">
|
||
<div class="hiding">
|
||
<h3>One Pass Loudness Normalization</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -af loudnorm=dual_mono=true -ar 48k <i>output_file</i></code></p>
|
||
<p>This will normalize the loudness of an input using one pass, which is quicker but less accurate than using two passes. This command uses the loudnorm filter defaults for target loudness. These defaults align well with PBS recommendations, but loudnorm does allow targeting of specific loudness levels. More information can be found at the <a href="https://ffmpeg.org/ffmpeg-filters.html#loudnorm" target="_blank">loudnorm documentation</a>.</p>
|
||
<p>Information about PBS loudness standards can be found in the <a href="http://www-tc.pbs.org/capt/Producing/TOS-2012-Pt2-Distribution.pdf" target="_blank">PBS Technical Operating Specifications</a> document. Information about EBU loudness standards can be found in the <a href="https://tech.ebu.ch/docs/r/r128-2014.pdf" target="_blank">EBU R 128</a> recommendation document.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-af loudnorm</dt><dd>activates the loudnorm filter with default settings</dd>
|
||
<dt>dual_mono=true</dt><dd>(optional) Use this for mono files meant to be played back on stereo systems for correct loudness. Not necessary for multi-track inputs.</dd>
|
||
<dt>-ar 48k</dt><dd>Sets the output sample rate to 48 kHz. (The loudnorm filter upsamples to 192 kHz so it is best to manually set a desired output sample rate).</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension for output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends one pass loudnorm -->
|
||
|
||
<!-- two pass loudnorm -->
|
||
<label class="recipe" for="loudnorm_two_pass">Two Pass Loudness Normalization</label>
|
||
<input type="checkbox" id="loudnorm_two_pass">
|
||
<div class="hiding">
|
||
<h3>Two Pass Loudness Normalization</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -af loudnorm=dual_mono=true:measured_I=<i>input_i</i>:measured_TP=<i>input_tp</i>:measured_LRA=<i>input_lra</i>:measured_thresh=<i>input_thresh</i>:offset=<i>target_offset</i>:linear=true -ar 48k <i>output_file</i></code></p>
|
||
<p>This command allows using the levels calculated using a <a href="#loudnorm_metadata">first pass of the loudnorm filter</a> to more accurately normalize loudness. This command uses the loudnorm filter defaults for target loudness. These defaults align well with PBS recommendations, but loudnorm does allow targeting of specific loudness levels. More information can be found at the <a href="https://ffmpeg.org/ffmpeg-filters.html#loudnorm" target="_blank">loudnorm documentation</a>.</p>
|
||
<p>Information about PBS loudness standards can be found in the <a href="http://www-tc.pbs.org/capt/Producing/TOS-2012-Pt2-Distribution.pdf" target="_blank">PBS Technical Operating Specifications</a> document. Information about EBU loudness standards can be found in the <a href="https://tech.ebu.ch/docs/r/r128-2014.pdf" target="_blank">EBU R 128</a> recommendation document.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-af loudnorm</dt><dd>activates the loudnorm filter with default settings</dd>
|
||
<dt>dual_mono=true</dt><dd>(optional) use this for mono files meant to be played back on stereo systems for correct loudness. Not necessary for multi-track inputs.</dd>
|
||
<dt>measured_I=<i>input_i</i></dt><dd>use the 'input_i' value (integrated loudness) from the first pass in place of input_i</dd>
|
||
<dt>measured_TP=<i>input_tp</i></dt><dd>use the 'input_tp' value (true peak) from the first pass in place of input_tp</dd>
|
||
<dt>measured_LRA=<i>input_lra</i></dt><dd>use the 'input_lra' value (loudness range) from the first pass in place of input_lra</dd>
|
||
<dt>measured_LRA=<i>input_thresh</i></dt><dd>use the 'input_thresh' value (threshold) from the first pass in place of input_thresh</dd>
|
||
<dt>offset=<i>target_offset</i></dt><dd>use the 'target_offset' value (offset) from the first pass in place of target_offset</dd>
|
||
<dt>linear=true</dt><dd>tells loudnorm to use linear normalization</dd>
|
||
<dt>-ar 48k</dt><dd>Sets the output sample rate to 48 kHz. (The loudnorm filter upsamples to 192 kHz so it is best to manually set a desired output sample rate).</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension for output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends two pass loudnorm -->
|
||
|
||
<!-- Fix A/V async 1 -->
|
||
<label class="recipe" for="avsync_aresample">Fix A/V sync issues by resampling audio</label>
|
||
<input type="checkbox" id="avsync_aresample">
|
||
<div class="hiding">
|
||
<h3>Fix AV Sync: Resample audio</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v copy -c:a pcm_s16le -af "aresample=async=1000" <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v copy</dt><dd>Copy all mapped video streams.</dd>
|
||
<dt>-c:a pcm_s16le</dt><dd>tells FFmpeg to encode the audio stream in 16-bit linear PCM (<a href="https://en.wikipedia.org/wiki/Endianness#Little-endian" target="_blank">little endian</a>)</dd>
|
||
<dt>-af "aresample=async=1000"</dt><dd>Uses the <a href="https://ffmpeg.org/ffmpeg-filters.html#aresample-1" target="_blank">aresample</a> filter to stretch/squeeze samples to given timestamps, with a maximum of 1000 samples per second compensation.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file. Try different file extensions such as mkv, mov, mp4, or avi.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Fix A/V async 1 -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="join-trim">Join, trim, or excerpt a video</h2>
|
||
|
||
<!-- Join files together -->
|
||
<label class="recipe" for="join_files">Join (concatenate) two or more files into a single file</label>
|
||
<input type="checkbox" id="join_files">
|
||
<div class="hiding">
|
||
<h3>Join files together</h3>
|
||
<p><code>ffmpeg -f concat -i mylist.txt -c copy <i>output_file</i></code></p>
|
||
<p>This command takes two or more files of the same file type and joins them together to make a single file. All that the program needs is a text file with a list specifying the files that should be joined. However, it only works properly if the files to be combined have the exact same codec and technical specifications. Be careful, FFmpeg may appear to have successfully joined two video files with different codecs, but may only bring over the audio from the second file or have other weird behaviors. Don’t use this command for joining files with different codecs and technical specs and always preview your resulting video file!</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f concat</dt><dd>forces ffmpeg to concatenate the files and to keep the same file format</dd>
|
||
<dt>-i <i>mylist.txt</i></dt><dd>path, name and extension of the input file. Per the <a href="https://www.ffmpeg.org/ffmpeg-formats.html#Options" target="_blank">FFmpeg documentation</a>, it is preferable to specify relative rather than absolute file paths, as allowing absolute file paths may pose a security risk.<br>
|
||
This text file contains the list of files to be concatenated and should be formatted as follows:
|
||
<pre>file '<i>./first_file.ext</i>'
|
||
file '<i>./second_file.ext</i>'
|
||
. . .
|
||
file '<i>./last_file.ext</i>'</pre>
|
||
In the above, <b>file</b> is simply the word "file". Straight apostrophes ('like this') rather than curved quotation marks (‘like this’) must be used to enclose the file paths.<br>
|
||
<b>Note:</b> If specifying absolute file paths in the .txt file, add <code>-safe 0</code> before the input file.<br>
|
||
e.g.: <code>ffmpeg -f concat -safe 0 -i mylist.txt -c copy <i>output_file</i></code></dd>
|
||
<dt>-c copy</dt><dd>use stream copy mode to re-mux instead of re-encode</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>For more information, see the <a href="https://trac.ffmpeg.org/wiki/Concatenate" target="_blank">FFmpeg wiki page on concatenating files</a>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Join files together -->
|
||
|
||
<!-- Split file into segments -->
|
||
<label class="recipe" for="segment_file">Split one file into several smaller segments</label>
|
||
<input type="checkbox" id="segment_file">
|
||
<div class="hiding">
|
||
<h3>Split file into segments</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c copy -map 0 -f segment -segment_time 60 -reset_timestamps 1 <i>output_file-%03d.mkv</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>Starts the command.</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>Takes in a normal file.</dd>
|
||
<dt>-c copy</dt><dd>Use stream copy mode to re-mux instead of re-encode.</dd>
|
||
<dt>-map 0</dt><dd>tells FFmpeg to map all streams of the input to the output.</dd>
|
||
<dt>-f segment</dt><dd>Use <a href="http://www.ffmpeg.org/ffmpeg-formats.html#toc-segment_002c-stream_005fsegment_002c-ssegment" target="_blank">segment muxer</a> for generating the output.</dd>
|
||
<dt>-segment_time 60</dt><dd>Set duration of each segment (in seconds). This example creates segments with max. duration of 60s each.</dd>
|
||
<dt>-reset_timestamps 1</dt><dd>Reset timestamps of each segment to 0. Meant to ease the playback of the generated segments.</dd>
|
||
<dt><i>output_file-%03d.mkv</i></dt>
|
||
<dd>
|
||
<p>Path, name and extension of the output file.<br>
|
||
In order to have an incrementing number in each segment filename, FFmpeg supports <a href="http://www.cplusplus.com/reference/cstdio/printf/" target="_blank">printf-style</a> syntax for a counter.</p>
|
||
<p>In this example, '%03d' means: 3-digits, zero-padded<br>
|
||
Examples:</p>
|
||
<ul>
|
||
<li><code>%03d</code>: 000, 001, 002, ... 999</li>
|
||
<li><code>%05d</code>: 00000, 00001, 00002, ... 99999</li>
|
||
<li><code>%d</code>: 0, 1, 2, 3, 4, ... 23, 24, etc. </li>
|
||
</ul>
|
||
</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Split file into segments -->
|
||
|
||
<!-- Trim -->
|
||
<label class="recipe" for="trim">Trim video</label>
|
||
<input type="checkbox" id="trim">
|
||
<div class="hiding">
|
||
<h3>Trim a video without re-encoding</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -ss 00:02:00 -to 00:55:00 -c copy -map 0 <i>output_file</i></code></p>
|
||
<p>This command allows you to create an excerpt from a video file without re-encoding the image data.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-ss 00:02:00</dt><dd>sets in point at 00:02:00</dd>
|
||
<dt>-to 00:55:00</dt><dd>sets out point at 00:55:00</dd>
|
||
<dt>-c copy</dt><dd>use stream copy mode (no re-encoding)<br>
|
||
<dt>-map 0</dt><dd>tells FFmpeg to map all streams of the input to the output.</dd>
|
||
<b>Note:</b> watch out when using <code>-ss</code> with <code>-c copy</code> if the source is encoded with an interframe codec (e.g., H.264). Since FFmpeg must split on i-frames, it will seek to the nearest i-frame to begin the stream copy.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>Variation: trim video by setting duration, by using <code>-t</code> instead of <code>-to</code></p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -ss 00:05:00 -t 10 -c copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>-ss 00:05:00 -t 10</dt><dd>Beginning five minutes into the original video, this command will create a 10-second-long excerpt.</dd>
|
||
</dl>
|
||
<p>Note: In order to keep the original timestamps, without trying to sanitise them, you may add the <code>-copyts</code> option.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Trim -->
|
||
|
||
<!-- Excerpt from beginning -->
|
||
<label class="recipe" for="excerpt_from_start">Create an excerpt, starting from the beginning of the file</label>
|
||
<input type="checkbox" id="excerpt_from_start">
|
||
<div class="hiding">
|
||
<h3>Excerpt from beginning</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -t <i>5</i> -c copy -map 0 <i>output_file</i></code></p>
|
||
<p>This command captures a certain portion of a video file, starting from the beginning and continuing for the amount of time (in seconds) specified in the script. This can be used to create a preview file, or to remove unwanted content from the end of the file. To be more specific, use timecode, such as 00:00:05.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-t <i>5</i></dt><dd>tells FFmpeg to stop copying from the input file after a certain time, and specifies the number of seconds after which to stop copying. In this case, 5 seconds is specified.</dd>
|
||
<dt>-c copy</dt><dd>use stream copy mode to re-mux instead of re-encode</dd>
|
||
<dt>-map 0</dt><dd>tells FFmpeg to map all streams of the input to the output.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Excerpt from beginning -->
|
||
|
||
<!-- Excerpt to end -->
|
||
<label class="recipe" for="excerpt_to_end">Create a new video file with the first five seconds trimmed off the original</label>
|
||
<input type="checkbox" id="excerpt_to_end">
|
||
<div class="hiding">
|
||
<h3>Excerpt to end</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -ss <i>5</i> -c copy -map 0 <i>output_file</i></code></p>
|
||
<p>This command copies a video file starting from a specified time, removing the first few seconds from the output. This can be used to create an excerpt, or remove unwanted content from the beginning of a video file.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-ss <i>5</i></dt><dd>tells FFmpeg what timecode in the file to look for to start copying, and specifies the number of seconds into the video that FFmpeg should start copying. To be more specific, you can use timecode such as 00:00:05.</dd>
|
||
<dt>-c copy</dt><dd>use stream copy mode to re-mux instead of re-encode</dd>
|
||
<dt>-map 0</dt><dd>tells FFmpeg to map all streams of the input to the output.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Excerpt to end -->
|
||
|
||
<!-- Excerpt from end -->
|
||
<label class="recipe" for="excerpt_from_end">Create a new video file with the final five seconds of the original</label>
|
||
<input type="checkbox" id="excerpt_from_end">
|
||
<div class="hiding">
|
||
<h3>Excerpt from end</h3>
|
||
<p><code>ffmpeg -sseof <i>-5</i> -i <i>input_file</i> -c copy -map 0 <i>output_file</i></code></p>
|
||
<p>This command copies a video file starting from a specified time before the end of the file, removing everything before from the output. This can be used to create an excerpt, or extract content from the end of a video file (e.g. for extracting the closing credits).</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-sseof <i>-5</i></dt><dd>This parameter must stay before the input file. It tells FFmpeg what timecode in the file to look for to start copying, and specifies the number of seconds from the end of the video that FFmpeg should start copying. The end of the file has index 0 and the minus sign is needed to reference earlier portions. To be more specific, you can use timecode such as -00:00:05. Note that in most file formats it is not possible to seek exactly, so FFmpeg will seek to the closest point before.</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c copy</dt><dd>use stream copy mode to re-mux instead of re-encode</dd>
|
||
<dt>-map 0</dt><dd>tells FFmpeg to map all streams of the input to the output.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Excerpt from end -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="interlacing">Work with interlaced video</h2>
|
||
|
||
<!-- NTSC to H.264 -->
|
||
<label class="recipe" for="ntsc_to_h264">Upscaled, pillar-boxed HD H.264 access files from SD NTSC source</label>
|
||
<input type="checkbox" id="ntsc_to_h264">
|
||
<div class="hiding">
|
||
<h3>Upscaled, Pillar-boxed HD H.264 Access Files from SD NTSC source</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -filter:v "yadif, scale=1440:1080:flags=lanczos, pad=1920:1080:(ow-iw)/2:(oh-ih)/2, format=yuv420p" <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx264</dt><dd>encodes video stream with libx264 (h264)</dd>
|
||
<dt>"</dt><dd>quotation mark to start filtergraph</dd>
|
||
<dt>yadif</dt><dd>deinterlacing filter (‘yet another deinterlacing filter’)<br>
|
||
By default, <a href="https://ffmpeg.org/ffmpeg-filters.html#yadif-1" target="_blank">yadif</a> will output one frame for each frame. Outputting one frame for each <i>field</i> (thereby doubling the frame rate) with <code>yadif=1</code> may produce visually better results.</dd>
|
||
<dt>scale=1440:1080:flags=lanczos</dt><dd>resizes the image to 1440x1080, using the Lanczos scaling algorithm, which is slower but better than the default bilinear algorithm.</dd>
|
||
<dt>pad=1920:1080:(ow-iw)/2:(oh-ih)/2</dt><dd>pads the area around the 4:3 input video to create a 16:9 output video</dd>
|
||
<dt>format=yuv420p</dt><dd>specifies a pixel format of Y′C<sub>B</sub>C<sub>R</sub> 4:2:0</dd>
|
||
<dt>"</dt><dd>quotation mark to end filtergraph</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p><b>Note:</b> the very same scaling filter also downscales a bigger image size into HD.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends NTSC to H.264 -->
|
||
|
||
<!-- Deinterlace video -->
|
||
<label class="recipe" for="deinterlace">Deinterlace video</label>
|
||
<input type="checkbox" id="deinterlace">
|
||
<div class="hiding">
|
||
<h3>Deinterlace a video</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf "yadif,format=yuv420p" <i>output_file</i></code></p>
|
||
<p>This command takes an interlaced input file and outputs a deinterlaced H.264 MP4.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx264</dt><dd>tells FFmpeg to encode the video stream as H.264</dd>
|
||
<dt>-vf</dt><dd>video filtering will be used (<code>-vf</code> is an alias of <code>-filter:v</code>)</dd>
|
||
<dt>"</dt><dd>start of filtergraph (see below)</dd>
|
||
<dt>yadif</dt><dd>deinterlacing filter (‘yet another deinterlacing filter’)<br>
|
||
By default, <a href="https://ffmpeg.org/ffmpeg-filters.html#yadif-1" target="_blank">yadif</a> will output one frame for each frame. Outputting one frame for each <i>field</i> (thereby doubling the frame rate) with <code>yadif=1</code> may produce visually better results.</dd>
|
||
<dt>,</dt><dd>separates filters</dd>
|
||
<dt>format=yuv420p</dt><dd>chroma subsampling set to 4:2:0<br>
|
||
By default, <code>libx264</code> will use a chroma subsampling scheme that is the closest match to that of the input. This can result in Y′C<sub>B</sub>C<sub>R</sub> 4:2:0, 4:2:2, or 4:4:4 chroma subsampling. QuickTime and most other non-FFmpeg based players can’t decode H.264 files that are not 4:2:0, therefore it’s advisable to specify 4:2:0 chroma subsampling.</dd>
|
||
<dt>"</dt><dd>end of filtergraph</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p> <code>"yadif,format=yuv420p"</code> is an FFmpeg <a href="https://trac.ffmpeg.org/wiki/FilteringGuide#FiltergraphChainFilterrelationship" target="_blank">filtergraph</a>. Here the filtergraph is made up of one filter chain, which is itself made up of the two filters (separated by the comma).<br>
|
||
The enclosing quote marks are necessary when you use spaces within the filtergraph, e.g. <code>-vf "yadif, format=yuv420p"</code>, and are included above as an example of good practice.</p>
|
||
<p><b>Note:</b> FFmpeg includes several deinterlacers apart from <a href="https://ffmpeg.org/ffmpeg-filters.html#yadif-1" target="_blank">yadif</a>: <a href="https://ffmpeg.org/ffmpeg-filters.html#bwdif" target="_blank">bwdif</a>, <a href="https://ffmpeg.org/ffmpeg-filters.html#w3fdif" target="_blank">w3fdif</a>, <a href="https://ffmpeg.org/ffmpeg-filters.html#kerndeint" target="_blank">kerndeint</a>, and <a href="https://ffmpeg.org/ffmpeg-filters.html#nnedi" target="_blank">nnedi</a>.</p>
|
||
<p>For more H.264 encoding options, see the latter section of the <a href="./index.html#transcode_h264">encode H.264 command</a>.</p>
|
||
<div class="sample-image">
|
||
<h2>Example</h2>
|
||
<p>Before and after deinterlacing:</p>
|
||
<img src="img/interlaced_video.png" alt="VLC screenshot of original interlaced video">
|
||
<img src="img/deinterlaced_video.png" alt="VLC screenshot of deinterlaced video">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Deinterlace video -->
|
||
|
||
<!-- Inverse telecine -->
|
||
<label class="recipe" for="inverse-telecine">Inverse telecine</label>
|
||
<input type="checkbox" id="inverse-telecine">
|
||
<div class="hiding">
|
||
<h3>Inverse telecine a video file</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v libx264 -vf "fieldmatch,yadif,decimate" <i>output_file</i></code></p>
|
||
<p>The inverse telecine procedure reverses the <a href="https://en.wikipedia.org/wiki/Three-two_pull_down" target="_blank">3:2 pull down</a> process, restoring 29.97fps interlaced video to the 24fps frame rate of the original film source.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-c:v libx264</dt><dd>encode video as H.264</dd>
|
||
<dt>-vf "fieldmatch,yadif,decimate"</dt><dd>applies these three video filters to the input video.<br>
|
||
<a href="https://ffmpeg.org/ffmpeg-filters.html#fieldmatch" target="_blank">Fieldmatch</a> is a field matching filter for inverse telecine - it reconstructs the progressive frames from a telecined stream.<br>
|
||
<a href="https://ffmpeg.org/ffmpeg-filters.html#yadif-1" target="_blank">Yadif</a> (‘yet another deinterlacing filter’) deinterlaces the video. (Note that FFmpeg also includes several other deinterlacers).<br>
|
||
<a href="https://ffmpeg.org/ffmpeg-filters.html#decimate-1" target="_blank">Decimate</a> deletes duplicated frames.</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p><code>"fieldmatch,yadif,decimate"</code> is an FFmpeg <a href="https://trac.ffmpeg.org/wiki/FilteringGuide#FiltergraphChainFilterrelationship" target="_blank">filtergraph</a>. Here the filtergraph is made up of one filter chain, which is itself made up of the three filters (separated by commas).<br>
|
||
The enclosing quote marks are necessary when you use spaces within the filtergraph, e.g. <code>-vf "fieldmatch, yadif, decimate"</code>, and are included above as an example of good practice.</p>
|
||
<p>Note that if applying an inverse telecine procedure to a 29.97i file, the output framerate will actually be 23.976fps.</p>
|
||
<p>This command can also be used to restore other framerates.</p>
|
||
<div class="sample-image">
|
||
<h2>Example</h2>
|
||
<p>Before and after inverse telecine:</p>
|
||
<img src="img/ivtc_originalvideo.gif" alt="GIF of original video">
|
||
<img src="img/ivtc_result.gif" alt="GIF of video after inverse telecine">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Inverse telecine -->
|
||
|
||
<!-- Set field order -->
|
||
<label class="recipe" for="set_field_order">Set field order for interlaced video</label>
|
||
<input type="checkbox" id="set_field_order">
|
||
<div class="hiding">
|
||
<h3>Change field order of an interlaced video</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -c:v <i>video_codec</i> -filter:v setfield=tff <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v setfield=tff</dt><dd>Sets the field order to top field first. Use <code>setfield=bff</code> for bottom field first.</dd>
|
||
<dt>-c:v <i>video_codec</i></dt><dd>As a video filter is used, it is not possible to use <code>-c copy</code>. The video must be re-encoded with whatever video codec is chosen, e.g. <code>ffv1</code>, <code>v210</code> or <code>prores</code>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Set field order -->
|
||
|
||
<!-- Check interlacement -->
|
||
<label class="recipe" for="check_interlacement">Identify interlacement patterns in a video file</label>
|
||
<input type="checkbox" id="check_interlacement">
|
||
<div class="hiding">
|
||
<h3>Check video file interlacement patterns</h3>
|
||
<p><code>ffmpeg -i <i>input file</i> -filter:v idet -f null -</code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v idet</dt><dd>This calls the <a href="https://ffmpeg.org/ffmpeg-filters.html#idet" target="_blank">idet (detect video interlacing type) filter</a>.</dd>
|
||
<dt>-f null</dt><dd>Video is decoded with the <code>null</code> muxer. This allows video decoding without creating an output file.</dd>
|
||
<dt>-</dt><dd>FFmpeg syntax requires a specified output, and <code>-</code> is just a place holder. No file is actually created.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Check interlacement -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="overlay">Overlay timecode or text</h2>
|
||
|
||
<!-- Text Watermark -->
|
||
<label class="recipe" for="text_watermark">Create opaque centered text watermark</label>
|
||
<input type="checkbox" id="text_watermark">
|
||
<div class="hiding">
|
||
<h3>Create centered, transparent text watermark</h3>
|
||
<p>E.g For creating access copies with your institutions name</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -vf drawtext="fontfile=<i>font_path</i>:fontsize=<i>font_size</i>:text=<i>watermark_text</i>:fontcolor=<i>font_colour</i>:alpha=0.4:x=(w-text_w)/2:y=(h-text_h)/2" <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-vf drawtext=</dt><dd>This calls the drawtext filter with the following options:
|
||
<dl>
|
||
<dt>fontfile=<i>font_path</i></dt><dd>Set path to font. For example in macOS: <code>fontfile=/Library/Fonts/AppleGothic.ttf</code></dd>
|
||
<dt>fontsize=<i>font_size</i></dt><dd>Set font size. <code>35</code> is a good starting point for SD. Ideally this value is proportional to video size, for example use ffprobe to acquire video height and divide by 14.</dd>
|
||
<dt>text=<i>watermark_text</i></dt><dd>Set the content of your watermark text. For example: <code>text='FFMPROVISR EXAMPLE TEXT'</code></dd>
|
||
<dt>fontcolor=<i>font_colour</i></dt><dd>Set colour of font. Can be a text string such as <code>fontcolor=white</code> or a hexadecimal value such as <code>fontcolor=0xFFFFFF</code></dd>
|
||
<dt>alpha=0.4</dt><dd>Set transparency value.</dd>
|
||
<dt>x=(w-text_w)/2:y=(h-text_h)/2</dt><dd>Sets <i>x</i> and <i>y</i> coordinates for the watermark. These relative values will centre your watermark regardless of video dimensions.</dd>
|
||
</dl>
|
||
Note: <code>-vf</code> is a shortcut for <code>-filter:v</code>.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Text watermark -->
|
||
|
||
<!-- Transparent Image Watermark -->
|
||
<label class="recipe" for="image_watermark">Overlay image watermark on video</label>
|
||
<input type="checkbox" id="image_watermark">
|
||
<div class="hiding">
|
||
<h3>Overlay image watermark on video</h3>
|
||
<p><code>ffmpeg -i <i>input_video file</i> -i <i>input_image_file</i> -filter_complex overlay=main_w-overlay_w-5:5 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_video_file</i></dt><dd>path, name and extension of the input video file</dd>
|
||
<dt>-i <i>input_image_file</i></dt><dd>path, name and extension of the image file</dd>
|
||
<dt>-filter_complex overlay=main_w-overlay_w-5:5</dt><dd>This calls the overlay filter and sets x and y coordinates for the position of the watermark on the video. Instead of hardcoding specific x and y coordinates, <code>main_w-overlay_w-5:5</code> uses relative coordinates to place the watermark in the upper right hand corner, based on the width of your input files. Please see the <a href="https://www.ffmpeg.org/ffmpeg-all.html#toc-Examples-102" target="_blank">FFmpeg documentation for more examples.</a></dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Image Watermark -->
|
||
|
||
<!-- Burn in timecode-->
|
||
<label class="recipe" for="burn_in_timecode">Burn in timecode</label>
|
||
<input type="checkbox" id="burn_in_timecode">
|
||
<div class="hiding">
|
||
<h3>Create a burnt in timecode on your image</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -vf drawtext="fontfile=<i>font_path</i>:fontsize=<i>font_size</i>:timecode=<i>starting_timecode</i>:fontcolor=<i>font_colour</i>:box=1:boxcolor=<i>box_colour</i>:rate=<i>timecode_rate</i>:x=(w-text_w)/2:y=h/1.2" <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-vf drawtext=</dt><dd>This calls the drawtext filter with the following options:
|
||
<dt>"</dt><dd>quotation mark to start drawtext filter command</dd>
|
||
<dt>fontfile=<i>font_path</i></dt><dd>Set path to font. For example in macOS: <code>fontfile=/Library/Fonts/AppleGothic.ttf</code></dd>
|
||
<dt>fontsize=<i>font_size</i></dt><dd>Set font size. <code>35</code> is a good starting point for SD. Ideally this value is proportional to video size, for example use ffprobe to acquire video height and divide by 14.</dd>
|
||
<dt>timecode=<i>starting_timecode</i></dt><dd>Set the timecode to be displayed for the first frame. Timecode is to be represented as <code>hh:mm:ss[:;.]ff</code>. Colon escaping is determined by O.S, for example in Ubuntu <code>timecode='09\\:50\\:01\\:23'</code>. Ideally, this value would be generated from the file itself using ffprobe.</dd>
|
||
<dt>fontcolor=<i>font_colour</i></dt><dd>Set colour of font. Can be a text string such as <code>fontcolor=white</code> or a hexadecimal value such as <code>fontcolor=0xFFFFFF</code></dd>
|
||
<dt>box=1</dt><dd>Enable box around timecode</dd>
|
||
<dt>boxcolor=<i>box_colour</i></dt><dd>Set colour of box. Can be a text string such as <code>fontcolor=black</code> or a hexadecimal value such as <code>fontcolor=0x000000</code></dd>
|
||
<dt>rate=<i>timecode_rate</i></dt><dd>Framerate of video. For example <code>25/1</code></dd>
|
||
<dt>x=(w-text_w)/2:y=h/1.2</dt><dd>Sets <i>x</i> and <i>y</i> coordinates for the timecode. These relative values will horizontally centre your timecode in the bottom third regardless of video dimensions.</dd>
|
||
<dt>"</dt><dd>quotation mark to end drawtext filter command</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file.</dd>
|
||
</dl>
|
||
<p>Note: <code>-vf</code> is a shortcut for <code>-filter:v</code>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Burn in timecode -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="create-images">Create thumbnails or GIFs</h2>
|
||
|
||
<!-- One thumbnail -->
|
||
<label class="recipe" for="one_thumbnail">Export one thumbnail per video file</label>
|
||
<input type="checkbox" id="one_thumbnail">
|
||
<div class="hiding">
|
||
<h3>One thumbnail</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -ss 00:00:20 -vframes 1 thumb.png</code></p>
|
||
<p>This command will grab a thumbnail 20 seconds into the video.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-ss <i>00:00:20</i></dt><dd>seeks video file to 20 seconds into the video</dd>
|
||
<dt>-vframes <i>1</i></dt><dd>sets the number of frames (in this example, one frame)</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends One thumbnail -->
|
||
|
||
<!-- Multi thumbnail -->
|
||
<label class="recipe" for="multi_thumbnail">Export many thumbnails per video file</label>
|
||
<input type="checkbox" id="multi_thumbnail">
|
||
<div class="hiding">
|
||
<h3>Many thumbnails</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -vf fps=1/60 out%d.png</code></p>
|
||
<p>This will grab a thumbnail every minute and output sequential png files.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-ss <i>00:00:20</i></dt><dd>seeks video file to 20 seconds into the video</dd>
|
||
<dt>-vf fps=1/60</dt><dd>Creates a filtergraph to use for the streams. The rest of the command identifies filtering by frames per second, and sets the frames per second at 1/60 (which is one per minute). Omitting this will output all frames from the video.</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file. In the example out%d.png where %d is a regular expression that adds a number (d is for digit) and increments with each frame (out1.png, out2.png, out3.png…). You may also chose a regular expression like out%04d.png which gives 4 digits with leading 0 (out0001.png, out0002.png, out0003.png, …).</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Multi thumbnail -->
|
||
|
||
<!-- Images to GIF -->
|
||
<label class="recipe" for="img_to_gif">Create GIF from still images</label>
|
||
<input type="checkbox" id="img_to_gif">
|
||
<div class="hiding">
|
||
<h3>Images to GIF</h3>
|
||
<p><code>ffmpeg -f image2 -framerate 9 -pattern_type glob -i <i>"input_image_*.jpg"</i> -vf scale=250x250 <i>output_file</i>.gif</code></p>
|
||
<p>This will convert a series of image files into a GIF.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f image2</dt><dd>forces input or output file format. <code>image2</code> specifies the image file demuxer.</dd>
|
||
<dt>-framerate 9</dt><dd>sets framerate to 9 frames per second</dd>
|
||
<dt>-pattern_type glob</dt><dd>tells FFmpeg that the following mapping should "interpret like a <a href="https://en.wikipedia.org/wiki/Glob_%28programming%29" target="_blank">glob</a>" (a "global command" function that relies on the * as a wildcard and finds everything that matches)</dd>
|
||
<dt>-i <i>"input_image_*.jpg"</i></dt><dd>maps all files in the directory that start with input_image_, for example input_image_001.jpg, input_image_002.jpg, input_image_003.jpg... etc.<br>
|
||
(The quotation marks are necessary for the above “glob” pattern!)</dd>
|
||
<dt>-vf scale=250x250</dt><dd>filter the video to scale it to 250x250; <code>-vf</code> is an alias for <code>-filter:v</code></dd>
|
||
<dt><i>output_file.gif</i></dt><dd>path and name of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Images to GIF -->
|
||
|
||
<!-- Create GIF -->
|
||
<label class="recipe" for="create_gif">Create GIF from a video</label>
|
||
<input type="checkbox" id="create_gif">
|
||
<div class="hiding">
|
||
<h3>Create GIF</h3>
|
||
<p>Create high quality GIF</p>
|
||
<p><code>ffmpeg -ss HH:MM:SS -i <i>input_file</i> -filter_complex "fps=10,scale=500:-1:flags=lanczos,palettegen" -t 3 <i>palette.png</i></code></p>
|
||
<p><code>ffmpeg -ss HH:MM:SS -i <i>input_file</i> -i palette.png -filter_complex "[0:v]fps=10, scale=500:-1:flags=lanczos[v], [v][1:v]paletteuse" -t 3 -loop 6 <i>output_file</i></code></p>
|
||
<p>The first command will use the palettegen filter to create a custom palette, then the second command will create the GIF with the paletteuse filter. The result is a high quality GIF.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-ss <i>HH:MM:SS</i></dt><dd>starting point of the GIF. If a plain numerical value is used it will be interpreted as seconds</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter_complex "fps=<i>framerate</i>, scale=<i>width</i>:<i>height</i>, palettegen"</dt><dd>a complex filtergraph.<br>
|
||
Firstly, the fps filter sets the frame rate.<br>
|
||
Then the scale filter resizes the image. You can specify both the width and the height, or specify a value for one and use a scale value of <i>-1</i> for the other to preserve the aspect ratio. (For example, <code>500:-1</code> would create a GIF 500 pixels wide and with a height proportional to the original video). In the first script above, <code>:flags=lanczos</code> specifies that the Lanczos rescaling algorithm will be used to resize the image.<br>
|
||
Lastly, the palettegen filter generates the palette.</dd>
|
||
<dt>-t <i>3</i></dt><dd>duration in seconds (here 3; can be specified also with a full timestamp, i.e. here 00:00:03)</dd>
|
||
<dt>-loop <i>6</i></dt><dd>sets the number of times to loop the GIF. A value of <i>-1</i> will disable looping. Omitting <i>-loop</i> will use the default, which will loop infinitely.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>The second command has a slightly different filtergraph, which breaks down as follows:</p>
|
||
<dl>
|
||
<dt>-filter_complex "[0:v]fps=10, scale=500:-1:flags=lanczos[v], [v][1:v]paletteuse"</dt><dd><code>[0:v]fps=10,scale=500:-1:flags=lanczos[v]</code>: applies the fps and scale filters described above to the first input file (the video).<br>
|
||
<code>[v][1:v]paletteuse"</code>: applies the <code>paletteuse</code> filter, setting the second input file (the palette) as the reference file.</dd>
|
||
</dl>
|
||
<p>Simpler GIF creation</p>
|
||
<p><code>ffmpeg -ss HH:MM:SS -i <i>input_file</i> -vf "fps=10,scale=500:-1" -t 3 -loop 6 <i>output_file</i></code></p>
|
||
<p>This is a quick and easy method. Dithering is more apparent than the above method using the palette filters, but the file size will be smaller. Perfect for that “legacy” GIF look.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Create GIF -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="create-video">Create a video from images</h2>
|
||
|
||
<!-- Images to video -->
|
||
<label class="recipe" for="images_2_video">Transcode an image sequence into uncompressed 10-bit video</label>
|
||
<input type="checkbox" id="images_2_video">
|
||
<div class="hiding">
|
||
<h3>Transcode an image sequence into uncompressed 10-bit video</h3>
|
||
<p><code>ffmpeg -f image2 -framerate 24 -i <i>input_file_%06d.ext</i> -c:v v210 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f image2</dt><dd>forces the image file de-muxer for single image files</dd>
|
||
<dt>-framerate 24</dt><dd>Sets the input framerate to 24 fps. The image2 demuxer defaults to 25 fps.</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file<br>
|
||
This must match the naming convention actually used! The regex %06d matches six digits long numbers, possibly with leading zeroes. This allows to read in ascending order, one image after the other, the full sequence inside one folder. For image sequences starting with 086400 (i.e. captured with a timecode starting at 01:00:00:00 and at 24 fps), add the flag <code>-start_number 086400</code> before <code>-i input_file_%06d.ext</code>. The extension for TIFF files is .tif or maybe .tiff; the extension for DPX files is .dpx (or eventually .cin for old files).</dd>
|
||
<dt>-c:v v210</dt><dd>encodes an uncompressed 10-bit video stream</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Images to video -->
|
||
|
||
<!-- Create video from image and audio -->
|
||
<label class="recipe" for="image-audio">Create video from image and audio</label>
|
||
<input type="checkbox" id="image-audio">
|
||
<div class="hiding">
|
||
<h3>Create a video from an image and audio file.</h3>
|
||
<p><code>ffmpeg -r 1 -loop 1 -i <i>image_file</i> -i <i>audio_file</i> -acodec copy -shortest -vf scale=1280:720 <i>output_file</i></code></p>
|
||
<p>This command will take an image file (e.g. image.jpg) and an audio file (e.g. audio.mp3) and combine them into a video file that contains the audio track with the image used as the video. It can be useful in a situation where you might want to upload an audio file to a platform like YouTube. You may want to adjust the scaling with -vf to suit your needs.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-r <i>1</i></dt><dd>set the framerate</dd>
|
||
<dt>-loop <i>1</i></dt><dd>loop the first input stream</dd>
|
||
<dt>-i <i>image_file</i></dt><dd>path, name and extension of the image file</dd>
|
||
<dt>-i <i>audio_file</i></dt><dd>path, name and extension of the audio file</dd>
|
||
<dt>-acodec copy</dt><dd>copy the audio. -acodec is an alias for -c:a</dd>
|
||
<dt>-shortest</dt><dd>finish encoding when the shortest input stream ends</dd>
|
||
<dt>-vf scale=1280:720</dt><dd>filter the video to scale it to 1280x720 for YouTube. -vf is an alias for -filter:v</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the video output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Create video from image and audio -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="filters-scopes">Use filters or scopes</h2>
|
||
|
||
<!-- abitscope -->
|
||
<label class="recipe" for="abitscope">Audio Bitscope</label>
|
||
<input type="checkbox" id="abitscope">
|
||
<div class="hiding">
|
||
<h3>Creates a visualization of the bits in an audio stream</h3>
|
||
<p><code>ffplay -f lavfi "amovie=<i>input_file</i>, asplit=2[out1][a], [a]abitscope=colors=purple|yellow[out0]"</code></p>
|
||
<p>This filter allows visual analysis of the information held in various bit depths of an audio stream. This can aid with identifying when a file that is nominally of a higher bit depth actually has been 'padded' with null information. The provided GIF shows a 16 bit WAV file (left) and then the results of converting that same WAV to 32 bit (right). Note that in the 32 bit version, there is still only information in the first 16 bits.</p>
|
||
<dl>
|
||
<dt>ffplay -f lavfi</dt><dd>starts the command and tells ffplay that you will be using the lavfi virtual device to create the input</dd>
|
||
<dt>"</dt><dd>quotation mark to start the lavfi filtergraph</dd>
|
||
<dt>amovie=<i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>asplit=2[out1][a]</dt><dd>splits the audio stream in two. One of these [a] will be passed to the filter, and the other [out1] will be the audible stream.</dd>
|
||
<dt>[a]abitscope=colors=purple|yellow[out0]</dt><dd>sends stream [a] into the abitscope filter, sets the colors for the channels to purple and yellow, and outputs the results to [out0]. This is what will be the visualization.</dd>
|
||
<dt>"</dt><dd>quotation mark to end the lavfi filtergraph</dd>
|
||
</dl>
|
||
<div class="sample-image">
|
||
<h2>Comparison of mono 16 bit and mono 16 bit padded to 32 bit.</h2>
|
||
<img src="img/16_32_abitscope.gif" alt="bit_scope_comparison">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends abitscope -->
|
||
|
||
<!-- astats -->
|
||
<label class="recipe" for="astats">Play a graphical output showing decibel levels of an input file</label>
|
||
<input type="checkbox" id="astats">
|
||
<div class="hiding">
|
||
<h3>Plays a graphical output showing decibel levels of an input file</h3>
|
||
<p><code>ffplay -f lavfi "amovie='input.mp3', astats=metadata=1:reset=1, adrawgraph=lavfi.astats.Overall.Peak_level:max=0:min=-30.0:size=700x256:bg=Black[out]"</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffplay to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter input virtual device</a></dd>
|
||
<dt>"</dt><dd>quotation mark to start the lavfi filtergraph</dd>
|
||
<dt>movie='<i>input.mp3</i>'</dt><dd>declares audio source file on which to apply filter</dd>
|
||
<dt>,</dt><dd>comma signifies the end of audio source section and the beginning of the filter section</dd>
|
||
<dt>astats=metadata=1</dt><dd>tells the astats filter to ouput metadata that can be passed to another filter (in this case adrawgraph)</dd>
|
||
<dt>:</dt><dd>divides between options of the same filter</dd>
|
||
<dt>reset=1</dt><dd>tells the filter to calculate the stats on every frame (increasing this number would calculate stats for groups of frames)</dd>
|
||
<dt>,</dt><dd>comma divides one filter in the chain from another</dd>
|
||
<dt>adrawgraph=lavfi.astats.Overall.Peak_level:max=0:min=-30.0</dt><dd>draws a graph using the overall peak volume calculated by the astats filter. It sets the max for the graph to 0 (dB) and the minimum to -30 (dB). For more options on data points that can be graphed see the <a href="https://ffmpeg.org/ffmpeg-filters.html#astats-1" target="_blank">FFmpeg astats documentation</a></dd>
|
||
<dt>size=700x256:bg=Black</dt><dd>sets the background color and size of the output</dd>
|
||
<dt>[out]</dt><dd>ends the filterchain and sets the output</dd>
|
||
<dt>"</dt><dd>quotation mark to end the lavfi filtergraph</dd>
|
||
</dl>
|
||
<div class="sample-image">
|
||
<h2>Example of filter output</h2>
|
||
<img src="img/astats_levels.gif" alt="astats example">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends astats -->
|
||
|
||
<!-- BRNG -->
|
||
<label class="recipe" for="brng">Identify pixels out of broadcast range</label>
|
||
<input type="checkbox" id="brng">
|
||
<div class="hiding">
|
||
<h3>Shows all pixels outside of broadcast range</h3>
|
||
<p><code>ffplay -f lavfi "movie='<i>input.mp4</i>', signalstats=out=brng:color=cyan[out]"</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffplay to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter input virtual device</a></dd>
|
||
<dt>"</dt><dd>quotation mark to start the lavfi filtergraph</dd>
|
||
<dt>movie='<i>input.mp4</i>'</dt><dd>declares video file source to apply filter</dd>
|
||
<dt>,</dt><dd>comma signifies closing of video source assertion and ready for filter assertion</dd>
|
||
<dt>signalstats=out=brng</dt><dd>tells ffplay to use the signalstats command, output the data, use the brng filter</dd>
|
||
<dt>:</dt><dd>indicates there’s another parameter coming</dd>
|
||
<dt>color=cyan[out]</dt><dd>sets the color of out-of-range pixels to cyan</dd>
|
||
<dt>"</dt><dd>quotation mark to end the lavfi filtergraph</dd>
|
||
</dl>
|
||
<div class="sample-image">
|
||
<h2>Example of filter output</h2>
|
||
<img src="./img/outside_broadcast_range.gif" alt="BRNG example">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends BRNG -->
|
||
|
||
<!-- Vectorscope -->
|
||
<label class="recipe" for="vectorscope">Vectorscope from video to screen</label>
|
||
<input type="checkbox" id="vectorscope">
|
||
<div class="hiding">
|
||
<h3>Plays vectorscope of video</h3>
|
||
<p><code>ffplay <i>input_file</i> -vf "split=2[m][v], [v]vectorscope=b=0.7:m=color3:g=green[v], [m][v]overlay=x=W-w:y=H-h"</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-vf</dt><dd>creates a filtergraph to use for the streams</dd>
|
||
<dt>"</dt><dd>quotation mark to start filtergraph</dd>
|
||
<dt>split=2[m][v]</dt><dd>Splits the input into two identical outputs and names them [m] and [v]</dd>
|
||
<dt>,</dt><dd>comma signifies there is another parameter coming</dd>
|
||
<dt>[v]vectorscope=b=0.7:m=color3:g=green[v]</dt><dd>asserts usage of the vectorscope filter and sets a light background opacity (b, alias for bgopacity), sets a background color style (m, alias for mode), and graticule color (g, alias for graticule)</dd>
|
||
<dt>,</dt><dd>comma signifies there is another parameter coming</dd>
|
||
<dt>[m][v]overlay=x=W-w:y=H-h</dt><dd>declares where the vectorscope will overlay on top of the video image as it plays</dd>
|
||
<dt>"</dt><dd>quotation mark to end filtergraph</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Vectorscope -->
|
||
|
||
<!--Side by Side Videos/Temporal Difference Filter-->
|
||
<label class="recipe" for="tempdif">Side by Side Videos/Temporal Difference Filter</label>
|
||
<input type="checkbox" id="tempdif">
|
||
<div class="hiding">
|
||
<h3>This will play two input videos side by side while also applying the temporal difference filter to them</h3>
|
||
<p><code>ffmpeg -i input01 -i input02 -filter_complex "[0:v:0]tblend=all_mode=difference128[a];[1:v:0]tblend=all_mode=difference128[b];[a][b]hstack[out]" -map [out] -f nut -c:v rawvideo - | ffplay -</code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input01</i> -i <i>input02</i></dt><dd>Designates the files to use for inputs one and two respectively</dd>
|
||
<dt>-filter_complex</dt><dd>Lets FFmpeg know we will be using a complex filter (this must be used for multiple inputs)</dd>
|
||
<dt>"</dt><dd>quotation mark to start filtergraph</dd>
|
||
<dt>[0:v:0]tblend=all_mode=difference128[a]</dt><dd>Applies the tblend filter (with the settings all_mode and difference128) to the first video stream from the first input and assigns the result to the output [a]</dd>
|
||
<dt>[1:v:0]tblend=all_mode=difference128[b]</dt><dd>Applies the tblend filter (with the settings all_mode and difference128) to the first video stream from the second input and assigns the result to the output [b]</dd>
|
||
<dt>[a][b]hstack[out]</dt><dd>Takes the outputs from the previous steps ([a] and [b] and uses the hstack (horizontal stack) filter on them to create the side by side output. This output is then named [out])</dd>
|
||
<dt>"</dt><dd>quotation mark to end filtergraph</dd>
|
||
<dt>-map [out]</dt><dd>Maps the output of the filter chain</dd>
|
||
<dt>-f nut</dt><dd>Sets the format for the output video stream to <a href="https://www.ffmpeg.org/ffmpeg-formats.html#nut" target="_blank">Nut</a></dd>
|
||
<dt>-c:v rawvideo</dt><dd>Sets the video codec of the output video stream to raw video</dd>
|
||
<dt>-</dt><dd>tells FFmpeg that the output will be piped to a new command (as opposed to a file)</dd>
|
||
<dt>|</dt><dd>Tells the system you will be piping the output of the previous command into a new command</dd>
|
||
<dt>ffplay -</dt><dd>Starts ffplay and tells it to use the pipe from the previous command as its input</dd>
|
||
</dl>
|
||
<div class="sample-image">
|
||
<h2>Example of filter output</h2>
|
||
<img src="img/tempdif.gif" alt="astats example">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Side by Side Videos/Temporal Difference Filter -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="metadata">View or strip metadata</h2>
|
||
|
||
<!-- Pull specs -->
|
||
<label class="recipe" for="pull_specs">Pull specs from video file</label>
|
||
<input type="checkbox" id="pull_specs">
|
||
<div class="hiding">
|
||
<h3>Pull specs from video file</h3>
|
||
<p><code>ffprobe -i <i>input_file</i> -show_format -show_streams -show_data -print_format xml</code></p>
|
||
<p>This command extracts technical metadata from a video file and displays it in xml.</p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-show_format</dt><dd>outputs file container informations</dd>
|
||
<dt>-show_streams</dt><dd>outputs audio and video codec informations</dd>
|
||
<dt>-show_data</dt><dd>adds a short “hexdump” to show_streams command output</dd>
|
||
<dt>-print_format</dt><dd>Set the output printing format (in this example “xml”; other formats include “json” and “flat”)</dd>
|
||
</dl>
|
||
<p>See also the <a href="http://www.ffmpeg.org/ffprobe.html" target="_blank"> FFmpeg documentation on ffprobe</a> for a full list of flags, commands, and options.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Pull specs -->
|
||
|
||
<!-- Strip metadata -->
|
||
<label class="recipe" for="strip_metadata">Strip metadata</label>
|
||
<input type="checkbox" id="strip_metadata">
|
||
<div class="hiding">
|
||
<h3>Strips metadata from video file</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -map_metadata -1 -c:v copy -c:a copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-map_metadata -1</dt><dd>sets metadata copying to -1, which copies nothing</dd>
|
||
<dt>-vcodec copy</dt><dd>copies video track</dd>
|
||
<dt>-acodec copy</dt><dd>copies audio track</dd>
|
||
<dt><i>output_file</i></dt><dd>Makes copy of original file and names output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Strip metadata -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="preservation">Preservation tasks</h2>
|
||
|
||
<!-- batch processing (Mac/Linux) -->
|
||
<label class="recipe" for="batch_processing_bash">Batch processing (Mac/Linux)</label>
|
||
<input type="checkbox" id="batch_processing_bash">
|
||
<div class="hiding">
|
||
<h3>Create Bash script to batch process with FFmpeg</h3>
|
||
<p>Bash scripts are plain text files saved with a .sh extension. This entry explains how they work with the example of a bash script named “Rewrap-MXF.sh”, which rewraps .mxf files in a given directory to .mov files.</p>
|
||
<p>“Rewrap-MXF.sh” contains the following text:</p>
|
||
<p><code>for file in *.mxf; do ffmpeg -i "$file" -map 0 -c copy "${file%.mxf}.mov"; done</code></p>
|
||
<dl>
|
||
<dt>for file in *.mxf</dt><dd>starts the loop, and states what the input files will be. Here, the FFmpeg command within the loop will be applied to all files with an extension of .mxf.<br>
|
||
The word ‘file’ is an arbitrary variable which will represent each .mxf file in turn as it is looped over.</dd>
|
||
<dt>do ffmpeg -i "$file"</dt><dd>carry out the following FFmpeg command for each input file.<br>
|
||
Per Bash syntax, within the command the variable is referred to by <b>“$file”</b>. The dollar sign is used to reference the variable ‘file’, and the enclosing quotation marks prevents reinterpretation of any special characters that may occur within the filename, ensuring that the original filename is retained.</dd>
|
||
<dt>-map 0</dt><dd>retain all streams</dd>
|
||
<dt>-c copy</dt><dd>enable stream copy (no re-encode)</dd>
|
||
<dt>"${file%.mxf}.mov";</dt><dd>retaining the original file name, set the output file wrapper as .mov</dd>
|
||
<dt>done</dt><dd>complete; all items have been processed.</dd>
|
||
</dl>
|
||
<p><b>Note:</b> the shell script (.sh file) and all .mxf files to be processed must be contained within the same directory, and the script must be run from that directory.<br>
|
||
Execute the .sh file with the command <code>sh Rewrap-MXF.sh</code>.</p>
|
||
<p>Modify the script as needed to perform different transcodes, or to use with ffprobe. :)</p>
|
||
<p>The basic pattern will look similar to this:<br>
|
||
<code>for item in *.ext; do ffmpeg -i $item <i>(FFmpeg options here)</i> "${item%.ext}_suffix.ext"</code></p>
|
||
<p>e.g., if an input file is bestmovie002.avi, its output will be bestmovie002_suffix.avi.</p>
|
||
<p>Variation: recursively process all MXF files in subdirectories using <code>find</code> instead of <code>for</code>:</p>
|
||
<p><code>find input_directory -iname "*.mxf" -exec ffmpeg -i {} -map 0 -c copy {}.mov \;</code></p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends batch processing (Mac/Linux) -->
|
||
|
||
<!-- batch processing (Windows) -->
|
||
<label class="recipe" for="batch_processing_win">Batch processing (Windows)</label>
|
||
<input type="checkbox" id="batch_processing_win">
|
||
<div class="hiding">
|
||
<h3>Create PowerShell script to batch process with FFmpeg</h3>
|
||
<p>As of Windows 10, it is possible to run Bash via <a href="https://msdn.microsoft.com/en-us/commandline/wsl/about" target="_blank">Bash on Ubuntu on Windows</a>, allowing you to use <a href="index.html#batch_processing_bash">bash scripting</a>. To enable Bash on Windows, see <a href="https://msdn.microsoft.com/en-us/commandline/wsl/install_guide" target="_blank">these instructions</a>.</p>
|
||
<p>On Windows, the primary native command line programme is <b>PowerShell</b>. PowerShell scripts are plain text files saved with a .ps1 extension. This entry explains how they work with the example of a PowerShell script named “rewrap-mp4.ps1”, which rewraps .mp4 files in a given directory to .mkv files.</p>
|
||
<p>“rewrap-mp4.ps1” contains the following text:</p>
|
||
<pre class="codeblock"><code>$inputfiles = ls *.mp4
|
||
foreach ($file in $inputfiles) {
|
||
$output = [io.path]::ChangeExtension($file, '.mkv')
|
||
ffmpeg -i $file -map 0 -c copy $output
|
||
}</code></pre>
|
||
<dl>
|
||
<dt>$inputfiles = ls *.mp4</dt><dd>Creates the variable <code>$inputfiles</code>, which is a list of all the .mp4 files in the current folder.<br>
|
||
In PowerShell, all variable names start with the dollar-sign character.</dd>
|
||
<dt>foreach ($file in $inputfiles)</dt><dd>Creates a loop and states the subsequent code block will be applied to each file listed in <code>$inputfiles</code>.<br>
|
||
<code>$file</code> is an arbitrary variable which will represent each .mp4 file in turn as it is looped over.</dd>
|
||
<dt>{</dt><dd>Opens the code block.</dd>
|
||
<dt>$output = [io.path]::ChangeExtension($file, '.mkv')</dt><dd>Sets up the output file: it will be located in the current folder and keep the same filename, but will have an .mkv extension instead of .mp4.</dd>
|
||
<dt>ffmpeg -i $file</dt><dd>Carry out the following FFmpeg command for each input file.<br>
|
||
<b>Note:</b> To call FFmpeg here as just ‘ffmpeg’ (rather than entering the full path to ffmpeg.exe), you must make sure that it’s correctly configured. See <a href="http://adaptivesamples.com/how-to-install-ffmpeg-on-windows/" target="_blank">this article</a>, especially the section ‘Add to Path’.</dd>
|
||
<dt>-map 0</dt><dd>retain all streams</dd>
|
||
<dt>-c copy</dt><dd>enable stream copy (no re-encode)</dd>
|
||
<dt>$output</dt><dd>The output file is set to the value of the <code>$output</code> variable declared above: i.e., the current file name with an .mkv extension.</dd>
|
||
<dt>}</dt><dd>Closes the code block.</dd>
|
||
</dl>
|
||
<p><b>Note:</b> the PowerShell script (.ps1 file) and all .mp4 files to be rewrapped must be contained within the same directory, and the script must be run from that directory.<p>
|
||
<p>Execute the .ps1 file by typing <code>.\rewrap-mp4.ps1</code> in PowerShell.</p>
|
||
<p>Modify the script as needed to perform different transcodes, or to use with ffprobe. :)</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends batch processing (Windows) -->
|
||
|
||
<!-- Create frame md5s -->
|
||
<label class="recipe" for="create_frame_md5s_v">Create MD5 checksums (video frames)</label>
|
||
<input type="checkbox" id="create_frame_md5s_v">
|
||
<div class="hiding">
|
||
<h3>Create MD5 checksums (video frames)</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -f framemd5 -an <i>output_file</i></code></p>
|
||
<p>This will create an MD5 checksum per video frame.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-f framemd5</dt><dd>library used to calculate the MD5 checksums</dd>
|
||
<dt>-an</dt><dd>ignores the audio stream (audio no)</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>You may verify an MD5 checksum file created this way by using a <a href="scripts/check_video_framemd5.sh" target="_blank">Bash script</a>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Create frame md5s -->
|
||
|
||
<!-- Create frame md5s (audio) -->
|
||
<label class="recipe" for="create_frame_md5s_a">Create MD5 checksums (audio samples)</label>
|
||
<input type="checkbox" id="create_frame_md5s_a">
|
||
<div class="hiding">
|
||
<h3>Create MD5 checksums (audio samples)</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -af "asetnsamples=n=48000" -f framemd5 -vn <i>output_file</i></code></p>
|
||
<p>This will create an MD5 checksum for each group of 48000 audio samples.<br>
|
||
The number of samples per group can be set arbitrarily, but it's good practice to match the samplerate of the media file (so you will get one checksum per second).</p>
|
||
<p>Examples for other samplerates:</p>
|
||
<ul>
|
||
<li>44.1 kHz: "asetnsamples=n=44100"</li>
|
||
<li>96 kHz: "asetnsamples=n=96000"</li>
|
||
</ul>
|
||
<p>Note: This filter trandscodes audio to 16 bit PCM by default. The generated framemd5s will represent this value. Validating these framemd5s will require using the same default settings.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-f framemd5</dt><dd>library used to calculate the MD5 checksums</dd>
|
||
<dt>-vn</dt><dd>ignores the video stream (video no)</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p>You may verify an MD5 checksum file created this way by using a <a href="scripts/check_audio_framemd5.sh" target="_blank">Bash script</a>.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Create frame md5s (audio) -->
|
||
|
||
<!-- QCTools Report -->
|
||
<label class="recipe" for="qctools">QCTools report (with audio)</label>
|
||
<input type="checkbox" id="qctools">
|
||
<div class="hiding">
|
||
<h3>Creates a QCTools report</h3>
|
||
<p><code>ffprobe -f lavfi -i "movie=<i>input_file</i>:s=v+a[in0][in1], [in0]signalstats=stat=tout+vrep+brng, cropdetect=reset=1:round=1, idet=half_life=1, split[a][b];[a]field=top[a1];[b]field=bottom, split[b1][b2];[a1][b1]psnr[c1];[c1][b2]ssim[out0];[in1]ebur128=metadata=1, astats=metadata=1:reset=1:length=0.4[out1]" -show_frames -show_versions -of xml=x=1:q=1 -noprivate | gzip > <i>input_file</i>.qctools.xml.gz</code></p>
|
||
<p>This will create an XML report for use in <a href="https://github.com/bavc/qctools" target="_blank">QCTools</a> for a video file with one video track and one audio track. See also the <a href="https://github.com/bavc/qctools/blob/master/docs/data_format.md#creating-a-qctools-document" target="_blank">QCTools documentation</a>.</p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffprobe to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i</dt><dd>input file and parameters</dd>
|
||
<dt>"movie=<i>input_file</i>:s=v+a[in0][in1], [in0]signalstats=stat=tout+vrep+brng, cropdetect=reset=1:round=1, idet=half_life=1, split[a][b];[a]field=top[a1];[b]field=bottom, split[b1][b2];[a1][b1]psnr[c1];[c1][b2]ssim[out0];[in1]ebur128=metadata=1, astats=metadata=1:reset=1:length=0.4[out1]"</dt>
|
||
<dd>This very large lump of commands declares the input file and passes in a request for all potential data signal information for a file with one video and one audio track</dd>
|
||
<dt>-show_frames</dt><dd>asks for information about each frame and subtitle contained in the input multimedia stream</dd>
|
||
<dt>-show_versions</dt><dd>asks for information related to program and library versions</dd>
|
||
<dt>-of xml=x=1:q=1</dt><dd>sets the data export format to XML</dd>
|
||
<dt>-noprivate</dt><dd>hides any private data that might exist in the file</dd>
|
||
<dt>| gzip</dt><dd>The | is to "pipe" (or push) the data into a compressed file format</dd>
|
||
<dt><code>></code></dt><dd>redirects the standard output (the data made by ffprobe about the video)</dd>
|
||
<dt><i>input_file</i>.qctools.xml.gz</dt><dd>names the zipped data output file, which can be named anything but needs the extension qctools.xml.gz for compatibility issues</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends QCTools Report -->
|
||
|
||
<!-- QCTools Report (no audio) -->
|
||
<label class="recipe" for="qctools_no_audio">QCTools report (no audio)</label>
|
||
<input type="checkbox" id="qctools_no_audio">
|
||
<div class="hiding">
|
||
<h3>Creates a QCTools report</h3>
|
||
<p><code>ffprobe -f lavfi -i "movie=<i>input_file</i>,signalstats=stat=tout+vrep+brng, cropdetect=reset=1:round=1, idet=half_life=1, split[a][b];[a]field=top[a1];[b]field=bottom,split[b1][b2];[a1][b1]psnr[c1];[c1][b2]ssim" -show_frames -show_versions -of xml=x=1:q=1 -noprivate | gzip > <i>input_file</i>.qctools.xml.gz</code></p>
|
||
<p>This will create an XML report for use in <a href="https://github.com/bavc/qctools" target="_blank">QCTools</a> for a video file with one video track and NO audio track. See also the <a href="https://github.com/bavc/qctools/blob/master/docs/data_format.md#creating-a-qctools-document" target="_blank">QCTools documentation</a>.</p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffprobe to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i</dt><dd>input file and parameters</dd>
|
||
<dt>"movie=<i>input_file</i>,signalstats=stat=tout+vrep+brng, cropdetect=reset=1:round=1, idet=half_life=1, split[a][b];[a]field=top[a1];[b]field=bottom,split[b1][b2];[a1][b1]psnr[c1];[c1][b2]ssim"</dt>
|
||
<dd>This very large lump of commands declares the input file and passes in a request for all potential data signal information for a file with one video and one audio track</dd>
|
||
<dt>-show_frames</dt><dd>asks for information about each frame and subtitle contained in the input multimedia stream</dd>
|
||
<dt>-show_versions</dt><dd>asks for information related to program and library versions</dd>
|
||
<dt>-of xml=x=1:q=1</dt><dd>sets the data export format to XML</dd>
|
||
<dt>-noprivate</dt><dd>hides any private data that might exist in the file</dd>
|
||
<dt>| gzip</dt><dd>The | is to "pipe" (or push) the data into a compressed file format</dd>
|
||
<dt><code>></code></dt><dd>redirects the standard output (the data made by ffprobe about the video)</dd>
|
||
<dt><i>input_file</i>.qctools.xml.gz</dt><dd>names the zipped data output file, which can be named anything but needs the extension qctools.xml.gz for compatibility issues</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends QCTools Report (no audio) -->
|
||
|
||
<!-- Check FFV1 fixity -->
|
||
<label class="recipe" for="check_FFV1_fixity">Check FFV1 fixity</label>
|
||
<input type="checkbox" id="check_FFV1_fixity">
|
||
<div class="hiding">
|
||
<h3>Check FFV1 Version 3 fixity</h3>
|
||
<p><code>ffmpeg -report -i <i>input_file</i> -f null -</code></p>
|
||
<p>This decodes your video and displays any CRC checksum mismatches. These errors will display in your terminal like this: <code>[ffv1 @ 0x1b04660] CRC mismatch 350FBD8A!at 0.272000 seconds</code></p>
|
||
<p>Frame CRCs are enabled by default in FFV1 Version 3.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-report</dt><dd>Dump full command line and console output to a file named <i>ffmpeg-YYYYMMDD-HHMMSS.log</i> in the current directory. It also implies <code>-loglevel verbose</code>.</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-f null</dt><dd>Video is decoded with the <code>null</code> muxer. This allows video decoding without creating an output file.</dd>
|
||
<dt>-</dt><dd>FFmpeg syntax requires a specified output, and <code>-</code> is just a place holder. No file is actually created. </dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Check FFV1 Fixity -->
|
||
|
||
<!-- Read/Extract EIA-608 Closed Captions -->
|
||
<label class="recipe" for="readeia608">Read/Extract EIA-608 Closed Captioning</label>
|
||
<input type="checkbox" id="readeia608">
|
||
<div class="hiding">
|
||
<h3>Read/Extract EIA-608 (Line 21) closed captioning</h3>
|
||
<p><code>ffprobe -f lavfi -i movie=<i>input_file</i>,readeia608 -show_entries frame=pkt_pts_time:frame_tags=lavfi.readeia608.0.line,lavfi.readeia608.0.cc,lavfi.readeia608.1.line,lavfi.readeia608.1.cc -of csv > <i>input_file</i>.csv</code></p>
|
||
<p>This command uses FFmpeg's <a href="https://ffmpeg.org/ffmpeg-filters.html#readeia608" target="_blank">readeia608</a> filter to extract the hexadecimal values hidden within <a href="https://en.wikipedia.org/wiki/EIA-608" target="_blank">EIA-608 (Line 21)</a> Closed Captioning, outputting a csv file. For more information about EIA-608, check out Adobe's <a href="https://www.adobe.com/content/dam/Adobe/en/devnet/video/pdfs/introduction_to_closed_captions.pdf" target="_blank">Introduction to Closed Captions</a>.</p>
|
||
<p>If hex isn't your thing, closed captioning <a href="http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/CC_CHARS.HTML" target="_blank">character</a> and <a href="http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/CC_CODES.HTML" target="_blank">code</a> sets can be found in the documentation for SCTools.</p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffprobe to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">libavfilter</a> input virtual device</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>input file and parameters</dd>
|
||
<dt>readeia608 -show_entries frame=pkt_pts_time:frame_tags=lavfi.readeia608.0.line,lavfi.readeia608.0.cc,lavfi.readeia608.1.line,lavfi.readeia608.1.cc -of csv</dt><dd>specifies the first two lines of video in which EIA-608 data (hexadecimal byte pairs) are identifiable by ffprobe, outputting comma separated values (CSV)</dd>
|
||
<dt>></dt><dd>redirects the standard output (the data created by ffprobe about the video)</dd>
|
||
<dt><i>output_file</i>.csv</dt><dd>names the CSV output file</dd>
|
||
</dl>
|
||
<div class="sample-image">
|
||
<h4>Example</h4>
|
||
<p>Side-by-side video with true EIA-608 captions on the left, zoomed in view of the captions on the right (with hex values represented). To achieve something similar with your own captioned video, try out the EIA608/VITC viewer in <a href="https://github.com/bavc/qctools" target="_blank">QCTools</a>.</p>
|
||
<img src="./img/eia608_captions.gif" alt="GIF of Closed Captions">
|
||
</div>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Read/Extract EIA-608 Closed Captions -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="test-files">Generate test files</h2>
|
||
|
||
<!-- Mandelbrot -->
|
||
<label class="recipe" for="mandelbrot">Make a mandelbrot test pattern video</label>
|
||
<input type="checkbox" id="mandelbrot">
|
||
<div class="hiding">
|
||
<h3>Makes a mandelbrot test pattern video</h3>
|
||
<p><code>ffmpeg -f lavfi -i mandelbrot=size=1280x720:rate=25 -c:v libx264 -t 10 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells FFmpeg to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i mandelbrot=size=1280x720:rate=25</dt><dd>asks for the <a href="https://ffmpeg.org/ffmpeg-filters.html#mandelbrot" target="_blank">mandelbrot test filter</a> as input. Adjusting the <code>size</code> and <code>rate</code> options allows you to choose a specific frame size and framerate.</dd>
|
||
<dt>-c:v libx264</dt><dd>transcodes video from rawvideo to H.264. Set <code>-pix_fmt</code> to <code>yuv420p</code> for greater H.264 compatibility with media players.</dd>
|
||
<dt>-t 10</dt><dd>specifies recording time of 10 seconds</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file. Try different file extensions such as mkv, mov, mp4, or avi.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Mandelbrot -->
|
||
|
||
<!-- SMPTE bars -->
|
||
<label class="recipe" for="smpte_bars">Make a SMPTE bars test pattern video</label>
|
||
<input type="checkbox" id="smpte_bars">
|
||
<div class="hiding">
|
||
<h3>Makes a SMPTE bars test pattern video</h3>
|
||
<p><code>ffmpeg -f lavfi -i smptebars=size=720x576:rate=25 -c:v prores -t 10 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells FFmpeg to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i smptebars=size=720x576:rate=25</dt><dd>asks for the <a href="https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-haldclutsrc_002c-nullsrc_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc" target="_blank">smptebars test filter</a> as input. Adjusting the <code>size</code> and <code>rate</code> options allows you to choose a specific frame size and framerate.</dd>
|
||
<dt>-c:v prores</dt><dd>transcodes video from rawvideo to Apple ProRes 4:2:2.</dd>
|
||
<dt>-t 10</dt><dd>specifies recording time of 10 seconds</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file. Try different file extensions such as mov or avi.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends SMPTE bars -->
|
||
|
||
<!-- Test pattern video -->
|
||
<label class="recipe" for="test">Make a test pattern video</label>
|
||
<input type="checkbox" id="test">
|
||
<div class="hiding">
|
||
<h3>Make a test pattern video</h3>
|
||
<p><code>ffmpeg -f lavfi -i testsrc=size=720x576:rate=25 -c:v v210 -t 10 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells FFmpeg to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">libavfilter</a> input virtual device</dd>
|
||
<dt>-i testsrc=size=720x576:rate=25</dt><dd>asks for the testsrc filter pattern as input. Adjusting the <code>size</code> and <code>rate</code> options allows you to choose a specific frame size and framerate. <br>
|
||
The different test patterns that can be generated are listed <a href="https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-haldclutsrc_002c-nullsrc_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc" target="_blank">here</a>.</dd>
|
||
<dt>-c:v v210</dt><dd>transcodes video from rawvideo to 10-bit Uncompressed Y′C<sub>B</sub>C<sub>R</sub> 4:2:2. Alter this setting to set your desired codec.</dd>
|
||
<dt>-t 10</dt><dd>specifies recording time of 10 seconds</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file. Try different file extensions such as mkv, mov, mp4, or avi.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Test pattern video -->
|
||
|
||
<!-- Play HD SMPTE bars -->
|
||
<label class="recipe" for="play_hd_smpte">Play HD SMPTE bars</label>
|
||
<input type="checkbox" id="play_hd_smpte">
|
||
<div class="hiding">
|
||
<h3>Play HD SMPTE bars</h3>
|
||
<p>Test an HD video projector by playing the SMPTE colour bars pattern.</p>
|
||
<p><code>ffplay -f lavfi -i smptehdbars=size=1920x1080</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffplay to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i smptehdbars=size=1920x1080</dt><dd>asks for the <a href="https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-haldclutsrc_002c-nullsrc_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc" target="_blank">smptehdbars filter pattern</a> as input and sets the HD resolution. This generates a colour bars pattern, based on the SMPTE RP 219–2002.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Play HD SMPTE bars -->
|
||
|
||
<!-- Play VGA SMPTE bars -->
|
||
<label class="recipe" for="play_vga_smpte">Play VGA SMPTE bars</label>
|
||
<input type="checkbox" id="play_vga_smpte">
|
||
<div class="hiding">
|
||
<h3>Play VGA SMPTE bars</h3>
|
||
<p>Test a VGA (SD) video projector by playing the SMPTE colour bars pattern.</p>
|
||
<p><code>ffplay -f lavfi -i smptebars=size=640x480</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffplay to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i smptebars=size=640x480</dt><dd>asks for the <a href="https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-haldclutsrc_002c-nullsrc_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc" target="_blank">smptebars filter pattern</a> as input and sets the VGA (SD) resolution. This generates a colour bars pattern, based on the SMPTE Engineering Guideline EG 1–1990.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Play VGA SMPTE bars -->
|
||
|
||
<!-- Sine wave -->
|
||
<label class="recipe" for="sine_wave">Generate a sine wave test audio file</label>
|
||
<input type="checkbox" id="sine_wave">
|
||
<div class="hiding">
|
||
<h3>Sine wave</h3>
|
||
<p>Generate a test audio file playing a sine wave.</p>
|
||
<p><code>ffmpeg -f lavfi -i "sine=frequency=1000:sample_rate=48000:duration=5" -c:a pcm_s16le <i>output_file</i>.wav</code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells FFmpeg to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>-i "sine=frequency=1000:sample_rate=48000:duration=5"</dt><dd>Sets the signal to 1000 Hz, sampling at 48 kHz, and for 5 seconds</dd>
|
||
<dt>-c:a pcm_s16le</dt><dd>encodes the audio codec in <code>pcm_s16le</code> (the default encoding for wav files). <code>pcm</code> represents pulse-code modulation format (raw bytes), <code>16</code> means 16 bits per sample, and <code>le</code> means "little endian"</dd>
|
||
<dt><i>output_file</i>.wav</dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Sine wave -->
|
||
|
||
<!-- SMPTE bars + Sine wave -->
|
||
<label class="recipe" for="smpte_bars_and_sine_wave">SMPTE bars + Sine wave audio</label>
|
||
<input type="checkbox" id="smpte_bars_and_sine_wave">
|
||
<div class="hiding">
|
||
<h3>SMPTE bars + Sine wave audio</h3>
|
||
<p>Generate a SMPTE bars test video + a 1kHz sine wave as audio testsignal.</p>
|
||
<p><code>ffmpeg -f lavfi -i smptebars=size=720x576:rate=25 -f lavfi -i "sine=frequency=1000:sample_rate=48000" -c:a pcm_s16le -t 10 -c:v ffv1 <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells FFmpeg to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">libavfilter</a> input virtual device</dd>
|
||
<dt>-i smptebars=size=720x576:rate=25</dt><dd>asks for the <a href="https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-haldclutsrc_002c-nullsrc_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc" target="_blank">smptebars test filter</a> as input. Adjusting the <code>size</code> and <code>rate</code> options allows you to choose a specific frame size and framerate.</dd>
|
||
<dt>-f lavfi</dt><dd>use libavfilter again, but now for audio</dd>
|
||
<dt>-i "sine=frequency=1000:sample_rate=48000"</dt><dd>Sets the signal to 1000 Hz, sampling at 48 kHz.</dd>
|
||
<dt>-c:a pcm_s16le</dt><dd>encodes the audio codec in <code>pcm_s16le</code> (the default encoding for wav files). <code>pcm</code> represents pulse-code modulation format (raw bytes), <code>16</code> means 16 bits per sample, and <code>le</code> means "little endian"</dd>
|
||
<dt>-t 10</dt><dd>specifies recording time of 10 seconds</dd>
|
||
<dt>-c:v ffv1</dt><dd>Encodes to <a href="https://en.wikipedia.org/wiki/FFV1" target="_blank">FFV1</a>. Alter this setting to set your desired codec.</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends SMPTE bars + Sine wave -->
|
||
|
||
<!-- Broken File -->
|
||
<label class="recipe" for="broken_file">Make a broken file</label>
|
||
<input type="checkbox" id="broken_file">
|
||
<div class="hiding">
|
||
<h3>Makes a broken test file</h3>
|
||
<p>Modifies an existing, functioning file and intentionally breaks it for testing purposes.</p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -bsf noise=1 -c copy <i>output_file</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>takes in a normal file</dd>
|
||
<dt>-bsf noise=1</dt><dd>sets bitstream filters for all to 'noise'. Filters can be set on specific filters using syntax such as <code>-bsf:v</code> for video, <code>-bsf:a</code> for audio, etc. The <a href="https://www.ffmpeg.org/ffmpeg-bitstream-filters.html#noise" target="_blank">noise filter</a> intentionally damages the contents of packets without damaging the container. This sets the noise level to 1 but it could be left blank or any number above 0.</dd>
|
||
<dt>-c copy</dt><dd>use stream copy mode to re-mux instead of re-encode</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Broken File -->
|
||
|
||
<!-- Game of Life -->
|
||
<label class="recipe" for="game_of_life">Conway's Game of Life</label>
|
||
<input type="checkbox" id="game_of_life">
|
||
<div class="hiding">
|
||
<h3>Conway's Game of Life</h3>
|
||
<p>Simulates <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Conway's Game of Life</a></p>
|
||
<p><code>ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_color=#00ff00,scale=1200:800</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffplay to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter</a> input virtual device</dd>
|
||
<dt>life=s=300x200</dt><dd>use the life filter and set the size of the video to 300x200</dd>
|
||
<dt>:</dt><dd>indicates there’s another parameter coming</dd>
|
||
<dt>mold=10:r=60:ratio=0.1</dt><dd>sets up the rules of the game: cell mold speed, video rate, and random fill ratio</dd>
|
||
<dt>:</dt><dd>indicates there’s another parameter coming</dd>
|
||
<dt>death_color=#C83232:life_color=#00ff00</dt><dd>specifies color for cell death and cell life; mold_color can also be set</dd>
|
||
<dt>,</dt><dd>comma signifies closing of video source assertion and ready for filter assertion</dd>
|
||
<dt>scale=1200:800</dt><dd>scale to 1280 width and 800 height</dd>
|
||
</dl>
|
||
<img src="img/life.gif" alt="GIF of above command">
|
||
<p>To save a portion of the stream instead of playing it back infinitely, use the following command:</p>
|
||
<p><code>ffmpeg -f lavfi -i life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_color=#00ff00,scale=1200:800 -t 5 <i>output_file</i></code></p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Game of Life -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="ocr">Use OCR</h2>
|
||
|
||
<!-- Show OCR -->
|
||
<label class="recipe" for="ocr_on_top">Play video with OCR</label>
|
||
<input type="checkbox" id="ocr_on_top">
|
||
<div class="hiding">
|
||
<h3>Plays video with OCR on top</h3>
|
||
<p>Note: ffmpeg must be compiled with the tesseract library for this script to work (<code>--with-tesseract</code> if using the <code>brew install ffmpeg</code> method).</p>
|
||
<p><code>ffplay input_file -vf "ocr,drawtext=fontfile=/Library/Fonts/Andale Mono.ttf:text=%{metadata\\\:lavfi.ocr.text}:fontcolor=white"</code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt><i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-vf</dt><dd>creates a filtergraph to use for the streams</dd>
|
||
<dt>"</dt><dd>quotation mark to start filtergraph</dd>
|
||
<dt>ocr,</dt><dd>tells ffplay to use ocr as source and the comma signifies that the script is ready for filter assertion</dd>
|
||
<dt>drawtext=fontfile=/Library/Fonts/Andale Mono.ttf</dt><dd>tells ffplay to drawtext and use a specific font (Andale Mono) when doing so</dd>
|
||
<dt>:</dt><dd>indicates there’s another parameter coming</dd>
|
||
<dt>text=%{metadata\\\:lavfi.ocr.text}</dt><dd>tells ffplay what text to use when playing. In this case, calls for metadata that lives in the lavfi.ocr.text library</dd>
|
||
<dt>:</dt><dd>indicates there’s another parameter coming</dd>
|
||
<dt>fontcolor=white</dt><dd>specifies font color as white</dd>
|
||
<dt>"</dt><dd>quotation mark to end filtergraph</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Show OCR -->
|
||
|
||
<!-- Export OCR -->
|
||
<label class="recipe" for="ffprobe_ocr">Export OCR from video to screen</label>
|
||
<input type="checkbox" id="ffprobe_ocr">
|
||
<div class="hiding">
|
||
<h3>Exports OCR data to screen</h3>
|
||
<p>Note: FFmpeg must be compiled with the tesseract library for this script to work (<code>--with-tesseract</code> if using the <code>brew install ffmpeg</code> method)</p>
|
||
<p><code>ffprobe -show_entries frame_tags=lavfi.ocr.text -f lavfi -i "movie=<i>input_file</i>,ocr"</code></p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt>-show_entries</dt><dd>sets a list of entries to show</dd>
|
||
<dt>frame_tags=lavfi.ocr.text</dt><dd>shows the <i>lavfi.ocr.text</i> tag in the frame section of the video</dd>
|
||
<dt>-f lavfi</dt><dd>tells ffprobe to use the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter input virtual device</a></dd>
|
||
<dt>-i "movie=<i>input_file</i>,ocr"</dt><dd>declares 'movie' as <i>input_file</i> and passes in the 'ocr' command</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Exports OCR -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="perceptual-similarity">Compare perceptual similarity of videos</h2>
|
||
|
||
<!-- Compare Video Fingerprints -->
|
||
<label class="recipe" for="compare_video_fingerprints">Compare Video Fingerprints</label>
|
||
<input type="checkbox" id="compare_video_fingerprints">
|
||
<div class="hiding">
|
||
<h3>Compare two video files for content similarity using perceptual hashing</h3>
|
||
<p><code>ffmpeg -i <i>input_one</i> -i <i>input_two</i> -filter_complex signature=detectmode=full:nb_inputs=2 -f null -</code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_one</i> -i <i>input_two</i></dt><dd>assigns the input files</dd>
|
||
<dt>-filter_complex</dt><dd>enables using more than one input file to the filter</dd>
|
||
<dt>signature=detectmode=full</dt><dd>Applies the signature filter to the inputs in 'full' mode. The other option is 'fast'.</dd>
|
||
<dt>nb_inputs=2</dt><dd>tells the filter to expect two input files</dd>
|
||
<dt>-f null -</dt><dd>Sets the output of FFmpeg to a null stream (since we are not creating a transcoded file, just viewing metadata).</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Compare Video Fingerprints -->
|
||
|
||
<!-- Generate Video Fingerprint -->
|
||
<label class="recipe" for="generate_video_fingerprint">Generate Video Fingerprint</label>
|
||
<input type="checkbox" id="generate_video_fingerprint">
|
||
<div class="hiding">
|
||
<h3>Generate a perceptual hash for an input video file</h3>
|
||
<p><code>ffmpeg -i <i>input</i> -vf signature=format=xml:filename="output.xml" -an -f null -</code></p>
|
||
<dl>
|
||
<dt>ffmpeg -i <i>input</i></dt><dd>starts the command using your input file</dd>
|
||
<dt>-vf signature=format=xml</dt><dd>applies the signature filter to the input file and sets the output format for the fingerprint to xml</dd>
|
||
<dt>filename="output.xml"</dt><dd>sets the output for the signature filter</dd>
|
||
<dt>-an</dt><dd>tells FFmpeg to ignore the audio stream of the input file</dd>
|
||
<dt>-f null -</dt><dd>Sets the FFmpeg output to a null stream (since we are only interested in the output generated by the filter).</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Generate Video Fingerprint -->
|
||
|
||
</div>
|
||
<div class="well">
|
||
<h2 id="other">Other</h2>
|
||
|
||
<!-- Play image sequence -->
|
||
<label class="recipe" for="play_im_seq">Play an image sequence</label>
|
||
<input type="checkbox" id="play_im_seq">
|
||
<div class="hiding">
|
||
<h3>Play an image sequence</h3>
|
||
<p>Play an image sequence directly as moving images, without having to create a video first.</p>
|
||
<p><code>ffplay -framerate 5 <i>input_file_%06d.ext</i></code></p>
|
||
<dl>
|
||
<dt>ffplay</dt><dd>starts the command</dd>
|
||
<dt>-framerate 5</dt><dd>plays image sequence at rate of 5 images per second<br>
|
||
<b>Note:</b> this low framerate will produce a slideshow effect.</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file<br>
|
||
This must match the naming convention used! The regex %06d matches six-digit-long numbers, possibly with leading zeroes. This allows the full sequence to be read in ascending order, one image after the other.<br>
|
||
The extension for TIFF files is .tif or maybe .tiff; the extension for DPX files is .dpx (or even .cin for old files). Screenshots are often in .png format.</dd>
|
||
</dl>
|
||
<p><b>Notes:</b></p>
|
||
<p>If <code>-framerate</code> is omitted, the playback speed depends on the images’ file sizes and on the computer’s processing power. It may be rather slow for large image files.</p>
|
||
<p>You can navigate durationally by clicking within the playback window. Clicking towards the left-hand side of the playback window takes you towards the beginning of the playback sequence; clicking towards the right takes you towards the end of the sequence.</p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Play image sequence -->
|
||
|
||
<!-- Split audio and video tracks -->
|
||
<label class="recipe" for="split_audio_video">Split audio and video tracks</label>
|
||
<input type="checkbox" id="split_audio_video">
|
||
<div class="hiding">
|
||
<h3>Split audio and video tracks</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -map 0:v:0 <i>video_output_file</i> -map 0:a:0 <i>audio_output_file</i></code></p>
|
||
<p>This command splits the original input file into a video and audio stream. The -map command identifies which streams are mapped to which file. To ensure that you’re mapping the right streams to the right file, run ffprobe before writing the script to identify which streams are desired.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-map 0:v:0</dt><dd>grabs the first video stream and maps it into:</dd>
|
||
<dt><i>video_output_file</i></dt><dd>path, name and extension of the video output file</dd>
|
||
<dt>-map 0:a:0</dt><dd>grabs the first audio stream and maps it into:</dd>
|
||
<dt><i>audio_output_file</i></dt><dd>path, name and extension of the audio output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Split audio and video tracks -->
|
||
|
||
<!-- Merge audio and video tracks -->
|
||
<label class="recipe" for="merge_audio_video">Merge audio and video tracks</label>
|
||
<input type="checkbox" id="merge_audio_video">
|
||
<div class="hiding">
|
||
<h3>Merge audio and video tracks</h3>
|
||
<p><code>ffmpeg -i <i>video_file</i> -i <i>audio_file</i> -map 0:v -map 1:a -c copy <i>output_file</i></code></p>
|
||
<p>This command takes a video file and an audio file as inputs, and creates an output file that combines the video stream in the first file with the audio stream in the second file.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>video_file</i></dt><dd>path, name and extension of the first input file (the video file)</dd>
|
||
<dt>-i <i>audio_file</i></dt><dd>path, name and extension of the second input file (the audio file)</dd>
|
||
<dt>-map <i>0:v</i></dt><dd>selects the video streams from the first input file</dd>
|
||
<dt>-map <i>1:a</i></dt><dd>selects the audio streams from the second input file</dd>
|
||
<dt>-c copy</dt><dd>copies streams without re-encoding</dd>
|
||
<dt><i>output_file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
<p><b>Note:</b> in the example above, the video input file is given prior to the audio input file. However, input files can be added any order, as long as they are indexed correctly when stream mapping with <code>-map</code>. See the entry on <a href="#stream-mapping">stream mapping</a>.</p>
|
||
<h4>Variation:</h4>
|
||
<p>Include the audio tracks from both input files with the following command:</p>
|
||
<p><code>ffmpeg -i <i>video_file</i> -i <i>audio_file</i> -map 0:v -map 0:a -map 1:a -c copy <i>output_file</i></code></p>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Merge audio and video tracks -->
|
||
|
||
<!-- Create ISO -->
|
||
<label class="recipe" for="create_iso">Create ISO files for DVD access</label>
|
||
<input type="checkbox" id="create_iso">
|
||
<div class="hiding">
|
||
<h3>Create ISO files for DVD access</h3>
|
||
<p>Create an ISO file that can be used to burn a DVD. Please note, you will have to install dvdauthor. To install dvd author using Homebrew run: <code>brew install dvdauthor</code></p>
|
||
<p><code>ffmpeg -i <i>input_file</i> -aspect <i>4:3</i> -target ntsc-dvd <i>output_file</i>.mpg</code></p>
|
||
<p>This command will take any file and create an MPEG file that dvdauthor can use to create an ISO.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-aspect 4:3</dt><dd>declares the aspect ratio of the resulting video file. You can also use 16:9.</dd>
|
||
<dt>-target ntsc-dvd</dt><dd>specifies the region for your DVD. This could be also pal-dvd.</dd>
|
||
<dt><i>output_file</i>.mpg</dt><dd>path and name of the output file. The extension must be <code>.mpg</code></dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Create ISO -->
|
||
|
||
<!-- Scene Detection using YDIF -->
|
||
<label class="recipe" for="csv-ydif">CSV with timecodes and YDIF</label>
|
||
<input type="checkbox" id="csv-ydif">
|
||
<div class="hiding">
|
||
<h3>Exports CSV for scene detection using YDIF</h3>
|
||
<p><code>ffprobe -f lavfi -i movie=<i>input_file</i>,signalstats -show_entries frame=pkt_pts_time:frame_tags=lavfi.signalstats.YDIF -of csv</code></p>
|
||
<p>This ffprobe command prints a CSV correlating timestamps and their YDIF values, useful for determining cuts.</p>
|
||
<dl>
|
||
<dt>ffprobe</dt><dd>starts the command</dd>
|
||
<dt>-f lavfi</dt><dd>uses the <a href="http://ffmpeg.org/ffmpeg-devices.html#lavfi" target="_blank">Libavfilter input virtual device</a> as chosen format</dd>
|
||
<dt>-i movie=<i>input file</i></dt><dd>path, name and extension of the input video file</dd>
|
||
<dt>,</dt><dd>comma signifies closing of video source assertion and ready for filter assertion</dd>
|
||
<dt>signalstats</dt><dd>tells ffprobe to use the signalstats command</dd>
|
||
<dt>-show_entries</dt><dd>sets list of entries to show per column, determined on the next line</dd>
|
||
<dt>frame=pkt_pts_time:frame_tags=lavfi.signalstats.YDIF</dt><dd>specifies showing the timecode (<code>pkt_pts_time</code>) in the frame stream and the YDIF section of the frame_tags stream</dd>
|
||
<dt>-of csv</dt><dd>sets the output printing format to CSV. <code>-of</code> is an alias of <code>-print_format</code>.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends sample Scene Detection using YDIF -->
|
||
|
||
<!-- Cover head switching noise -->
|
||
<label class="recipe" for="cover_head">Cover head switching noise</label>
|
||
<input type="checkbox" id="cover_head">
|
||
<div class="hiding">
|
||
<h3>Cover head switching noise</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> -filter:v drawbox=w=iw:h=7:y=ih-h:t=max <i>output_file</i></code></p>
|
||
<p>This command will draw a black box over a small area of the bottom of the frame, which can be used to cover up head switching noise.</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input_file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>-filter:v drawbox=</dt>
|
||
<dd>This calls the drawtext filter with the following options:
|
||
<dl>
|
||
<dt>w=in_w</dt><dd>Width is set to the input width. Shorthand for this command would be w=iw</dd>
|
||
<dt>h=7</dt><dd>Height is set to 7 pixels.</dd>
|
||
<dt>y=ih-h</dt><dd>Y represents the offset, and ih-h sets it to the input height minus the height declared in the previous parameter, setting the box at the bottom of the frame.</dd>
|
||
<dt>t=max</dt><dd>T represents the thickness of the drawn box. Default is 3.</dd>
|
||
</dl>
|
||
</dd>
|
||
<dt><i>output_file</i></dt><dd>path and name of the output file</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends Cover head switching noise -->
|
||
|
||
<!-- Record and live-stream simultaneously -->
|
||
<label class="recipe" for="record-and-stream">Record and live-stream simultaneously</label>
|
||
<input type="checkbox" id="record-and-stream">
|
||
<div class="hiding">
|
||
<h3>Record and live-stream simultaneously</h3>
|
||
<p><code>ffmpeg -re -i <i>${INPUTFILE}</i> -map 0 -flags +global_header -vf scale="1280:-1,format=yuv420p" -pix_fmt yuv420p -level 3.1 -vsync passthrough -crf 26 -g 50 -bufsize 3500k -maxrate 1800k -c:v libx264 -c:a aac -b:a 128000 -r:a 44100 -ac 2 -t ${STREAMDURATION} -f tee <i>"[movflags=+faststart]${TARGETFILE}|[f=flv]${STREAMTARGET}"</i></code></p>
|
||
<p>I use this script to stream to a RTMP target and record the stream locally as .mp4 with only one ffmpeg-instance.</p>
|
||
<p>As input, I use <code>bmdcapture</code> which is piped to ffmpeg. But it can also be used with a static videofile as input.</p>
|
||
<p>The input will be scaled to 1280px width, maintaining height. Also the stream will stop after a given time (see <code>-t</code> option.)</p>
|
||
<h4>Notes</h4>
|
||
<ol>
|
||
<li>I recommend to use this inside a shell script - then you can define the variables <code>${INPUTFILE}</code>, <code>${STREAMDURATION}</code>, <code>${TARGETFILE}</code>, and <code>${STREAMTARGET}</code>.</li>
|
||
<li>This is in daily use to live-stream a real-world TV show. No errors for nearly 4 years. Some parameters were found by trial-and-error or empiric testing. So suggestions/questions are welcome.</li>
|
||
</ol>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-re</dt><dd>Read input at native framerate</dd>
|
||
<dt>-i input.mov</dt><dd>The input file. Can also be a <code>-</code> to use STDIN if you pipe in from webcam or SDI.</dd>
|
||
<dt>-map 0</dt><dd>map ALL streams from input file to output</dd>
|
||
<dt>-flags +global_header</dt><dd>Don't place extra data in every keyframe</dd>
|
||
<dt>-vf scale="1280:-1"</dt><dd>Scale to 1280 width, maintain aspect ratio.</dd>
|
||
<dt>-pix_fmt yuv420p</dt><dd>convert to 4:2:0 chroma subsampling scheme</dd>
|
||
<dt>-level 3.1</dt><dd>H264 Level (defines some thresholds for bitrate)</dd>
|
||
<dt>-vsync passthrough</dt><dd>Each frame is passed with its timestamp from the demuxer to the muxer.</dd>
|
||
<dt>-crf 26</dt><dd>Constant rate factor - basically the quality</dd>
|
||
<dt>-g 50</dt><dd>GOP size.</dd>
|
||
<dt>-bufsize 3500k</dt><dd>Ratecontrol buffer size (~ maxrate x2)</dd>
|
||
<dt>-maxrate 1800k</dt><dd>Maximum bit rate</dd>
|
||
<dt>-c:v libx264</dt><dd>encode output video stream as H.264</dd>
|
||
<dt>-c:a aac</dt><dd>encode output audio stream as AAC</dd>
|
||
<dt>-b:a 128000</dt><dd>The audio bitrate</dd>
|
||
<dt>-r:a 44100</dt><dd>The audio samplerate</dd>
|
||
<dt>-ac 2</dt><dd>Two audio channels</dd>
|
||
<dt>-t ${STREAMDURATION}</dt><dd>Time (in seconds) after which the stream should automatically end.</dd>
|
||
<dt>-f tee</dt><dd>Use multiple outputs. Outputs defined below.</dd>
|
||
<dt>"[movflags=+faststart]target-file.mp4|[f=flv]rtmp://stream-url/stream-id"</dt><dd>The outputs, separated by a pipe (|). The first is the local file, the second is the live stream. Options for each target are given in square brackets before the target.</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- END Record and live-stream at the same time -->
|
||
|
||
<!-- View Subprogram Info -->
|
||
<label class="recipe" for="view_subprogram_info">View FFmpeg subprogram information</label>
|
||
<input type="checkbox" id="view_subprogram_info">
|
||
<div class="hiding">
|
||
<h3>View information about a specific decoder, encoder, demuxer, muxer, or filter</h3>
|
||
<p><code>ffmpeg -h <i>type=name</i></code></p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-h</dt><dd>Call the help option</dd>
|
||
<dt>type=name</dt>
|
||
<dd>tells FFmpeg which kind of option you want, for example:
|
||
<ul>
|
||
<li><code>encoder=libx264</code></li>
|
||
<li><code>decoder=mp3</code></li>
|
||
<li><code>muxer=matroska</code></li>
|
||
<li><code>demuxer=mov</code></li>
|
||
<li><code>filter=crop</code></li>
|
||
</ul>
|
||
</dd>
|
||
</dl>
|
||
<p class="link"></p>
|
||
</div>
|
||
<!-- ends View Subprogram info -->
|
||
</div>
|
||
</div><!-- ends "content" -->
|
||
|
||
<!-- sample example -->
|
||
<!-- <label class="recipe" for="*****unique name*****">*****Title****</label>
|
||
<input type="checkbox" id="*****unique name*****">
|
||
<div class="hiding">
|
||
Change the above data-target field, the hover-over description, the button text, and the below div ID
|
||
<h3>*****Longer title*****</h3>
|
||
<p><code>ffmpeg -i <i>input_file</i> *****code goes here***** <i>output_file</i></code></p>
|
||
<p>This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info! This is all about info!</p>
|
||
<dl>
|
||
<dt>ffmpeg</dt><dd>starts the command</dd>
|
||
<dt>-i <i>input file</i></dt><dd>path, name and extension of the input file</dd>
|
||
<dt>*****parameter*****</dt><dd>*****comments*****</dd>
|
||
<dt><i>output file</i></dt><dd>path, name and extension of the output file</dd>
|
||
</dl>
|
||
</div> -->
|
||
<!-- ends sample example -->
|
||
|
||
<footer class="footer">
|
||
<p>Made with ♥ at <a href="http://wiki.curatecamp.org/index.php/Association_of_Moving_Image_Archivists_%26_Digital_Library_Federation_Hack_Day_2015" target="_blank">AMIA #AVhack15</a>! Contribute to the project via <a href="https://github.com/amiaopensource/ffmprovisr">our GitHub page</a>!</p>
|
||
</footer>
|
||
</div><!-- ends "grid" -->
|
||
</body>
|
||
</html>
|