Thursday, February 1, 2024

Yolo AutoCropping Presentation Videos


 

As camera resolutions continue to improve the feasibility of capturing a full scene of a classroom, lecture, presentation hall, or the such and autonomously focusing attention on the presenter becomes more practical.  Generally, a camera operator pans and zooms in on the presenter as they make their way around the stage to draw the audience attention to the intended target.  Professionally filmed videos draw the audiences attention to the speaker and their production quality contributes to a more informative presentation.

Wide-angle, static camera positions are an alternative for capturing presentations but generally fail to draw the audience attention to the speaker.  With robust object-detection, the position of the presenter can be automated and thru the use of auto-cropping the presenter can offer a budget-friendly alternative to more professional video production facilities.


YOLO (You Only Look Once) takes a different approach from classic computer vision by utilizing a classifier as a detector.  Authored by Joseph Redmon at the University of Washington, YOLO sub-samples and image into regions, assumes each region has an object and executes a classifier on each region, then merges the classifier groups into a list of final objects.  

 

Below is a proof-of-concept utilizing YOLO in an auto-cropping manner.  The wide-angle source video is used as input, object-detection is focused on the front of the room, detects the presenter and auto-cropped around the presenter.  Once the presenter location is available, we use a variety of means to 'pan the camera', the first by snapping to the presenter location, the second by smoothing the camera motion by incorporating a 2-dimensional shaper, the third using the shaper but only moving the camera when the presenter nears the edges of the current crop window.

Each mechanism is a rough implementation, focused on rapid proof-of-concept rather than optimal results, but you get the idea.  


The source video was found on here; Minnebar7

 

 



Tuesday, January 30, 2024

Published My First Python Package


 

 

I started dabbling with Python back in 2012'ish, using it pretty regularly over the years but generally keeping my projects close to home.  Recently, I dipped my toe into publishing a Python package, out to the known universe.

Back in the late 90's, the Precambrian Digital Age, I took a couple courses that continue to pique my interest time and time again.  Parallel processing was primarily constrained to supercomputers like the Cray-1 that was homed in a nearby lab on campus, on full display behind a full-glass wall.  A workhorse which eagerly awaited computationally intensive parallelized programs. 

A customized version of Fortran, its vocabulary, the Computer Science department rarely used it, aerospace and atmospheric sciences most heavily used the system.

The second course, Distributed Operating Systems, taken a bit later seemed to pair well with this budding interest in high performance computing.  Beowulf clusters, commodity-grade networked computers running Linux, could be created from RadioShack-provided equipment fueled by inspiration.   Cloud computing, virtual machines and even network-intensive applications hadn't breached the digital horizon, but small-cluster networked labs provided inspiration that one day multitudes of computing assets would one day join hands in forming highly networked, parallel, distributed systems that can be considered common today.

While robust and reliable distributed systems are highly sought after, engineering them is plagued with challenges.  Failed requests could be due to loss of the sent message, the loss of the response, the destination service abruptly terminating, relocation of the service, a over-tasked memory/cpu that slows the response,....or any number of other factors.  Python and ZeroMQ pair well to allow the creation of a distributed system framework which inspired my budding project.

dividere UG

The public project repository is located at:

https://github.com/lipeltgm/dividere


This is my first cut at publishing a python package, I tried to apply good design, test and documentation principles along the way.  One particular challenge I encountered is that the package dependencies require a version of Protobuf that isn't currently available via 'normal channels'.  I'm hoping in time that complication will self-correct when compliant versions become the default.

Until then, it likely will require manual installation of protobuff-v3.19 (or later) before installing via pip3 from pypi:

https://pypi.org/project/dividere/ 

$ pip3 install dividere


With the foundation in place, I'm intending on extending the framework to support more reliable messaging, database components, robust failover detection and recovery.  

More to come in the future, fingers-crossed.


Monday, January 29, 2024

Embarking on Authoring a Computer Science Book

 


Like many folks, I've occasionally been drawn to 'write a book', despite a real need for it.  Mid 2020, I had a couple inspiring computer science majors contact me via Reddit for advice, mostly involving 'what is CS' and 'How do I get into CS', but one exchange left me puzzled.  A young man from Ireland, just leaving high school was accepted into university but later rejected as a result of Covid reduction in campus actions.  He asked what he could do to get a head-start in self-study or prepare for the industry in the event he never gets in.  My suggestion was simply; "go to the university bookstore, find the CS textbooks, buy them and begin self-study".  I heavily encouraged going to university, and stressed that I doubt I'd be a professional in the industry had I not done so, but as a plan-B aligning your self-study with the university curriculum would be better than adhoc YouTube, influencer offerings, or code camps.  

To my surprise, I quickly found I'm out of touch with how universities teach as of late.  Many no longer have physical textbooks, or virtual ones for that matter, instead they teach via interactive websites with automated grading.  Restricted to university students closed the avenue to my suggestion.  So, over a few days I got a bug to write a book, a collaboration of my CS university teachings in a manner I had wished it was presented to me.  Worse case, after spending some time on it, I'd know how lofty an effort.  

On and off, I return to this passion project unsure of it's practicality but it helps rekindle my love for this career.

 

Attached is a snippet of my work in progress, not even titled yet;

WIP


I'd welcome any feedback on the chapter as well as experiences from anyone who has authored and published a book.

Friday, January 26, 2024

C++ Database ORM Project

 


 

Systems that utilize a database benefit from an automated means of translating database CRUD operations to application language.  

Products like 'ObjectStore' aim to provide mechanisms to exchange data to/from applications to the database in a seamless fashion.

This can be done by providing the means of converting C++ objects into SQL query/insert/update commands and converting query responses back into C++ objects.  

My SQL-fu being significantly rusty, I spend a bit of time attempting to create an ORM/MySql project go get a better understanding on how such a product could be created.

If we take the tact of most 'language-independent' products, we can start with a language-independent intermediary language, one that allows us to define the type of objects we wish to store/retrieve from the database.

A db-object file (e.g. MyDb.odb) can specify a database object as follows:

dbclass MyRecord002
  float val01 as key;
end;

This odb file can then be pre-processed, creating a language-dependent library  components (MyDb.h, MyDb.cpp) which can then be used by applications directly.  Updates to the library component can automatically be applied in the database.  The linked association between the C++ object and the database are enforced by constructors and access methods.

A bit of proof-of-concept at this time, works for a handful of data types {int, float, long, text, char(X)}, soon to add date/time.  At this time, constrained to 'flat' datatypes, but *fingers-crossed* to work for nested datatypes in the future.

https://github.com/fsk-software/pub/tree/master/DbObjOverlay

Recently came across Wt::Dbo reference as well, haven't had a chance to take a look yet; https://www.webtoolkit.eu/wt/doc/tutorial/dbo.html


Monday, April 11, 2022

Extracting Video Information w/FFProbe



It’s an interesting thing about tools, when they deliver what you need from them you’re often uninterested in ’how the sausage is made’, but digging into the details often re-enforces your understanding in the end. Kinda like eating your broccoli, its oftentimes good for you, and likely you’ll be better off having done it.

There is just shy of a bazillion things you can learn about FFmpeg and the video/audio domain, we’re going to spend just a little bit of time trying to understand some of the details readily available to us and hopefully understand the tooling and domain just a little bit more than when we started.

Saddle up, grab a beer and ’read on’ fellow digital cowboy. FFmpeg typically comes paired with a useful utility called ffprobe, a media prober, which we’ll use to examine media files and pull out interesting nuggets of information.

FFprobe, like FFmpeg, is pretty verbose when run writing a ton of debug information to stderr. This proves useful when it’s needed, but burdensome when not. For our uses we will quiet the utilities down by specifying -loglevel quiet. 

Let’s start by examining our media files container.
$ ffprobe - loglevel quiet - show_format BigBuckBunny . mp4
[ FORMAT ]
filename = BigBuckBunny . mp4
nb_streams =2
nb_programs =0
format_name = matroska , webm
format_long_name = Matroska / WebM
start_time = -0.007000
duration =596.501000
size =107903686
bit_rate =1447155
probe_score =100
TAG : C OMPATI BLE_BR ANDS = iso6avc1mp41
TAG : MAJOR_BRAND = dash
TAG : MINOR_VERSION =0
TAG : ENCODER = Lavf56 .40.101
[/ FORMAT ]

As you’re likely aware, a media container is simply a file that contains the video(s), audio(s), and subtitle(s). Media-wide properties, like file size, tags, length....are often available as well as user-defined tags (like GPS, date,...). By default, show format will show all properties of the media  container.Sometimes, you may wish to limit the fields to ones you’re particularly interested in, like duration and size. You’ll notice the user-tags are displayed despite not being specified, i’ve not found a way to suppress them directly so they can simply be ignored.

$ ffprobe - loglevel quiet - show_format BigBuckBunny .
mp4 - show_entries format = duration , size
[ FORMAT ]
duration =596.501000
size =107903686
TAG : COMPATIBLE_BRANDS = iso6avc1mp41
TAG : MAJOR_BRAND = dash
TAG : MINOR_VERSION =0
TAG : ENCODER = Lavf56 .40.101
[/ FORMAT ]

Cool, but not particularly interesting, and really nothing a filemanager couldn’t show you with a simple right-click.

Let’s dig a bit deeper by examining the video/audio frames. By specifying the show frames we can extract debug information for each frame in the media file. Let’s peek at the first few dozen lines.

$ ffprobe - loglevel quiet - show_frames BigBuckBunny .
mp4
[ FRAME ]
media_type = video
stream_index =0
key_frame =1
pkt_pts =0
pkt_pts_time =0.000000
pkt_dts =0
pkt_dts_time =0.000000
best_effort_timestamp =0
best_effort_timestamp_time =0.000000
pkt_duration =41
pkt_durat ion_time =0.041000
pkt_pos =1111
pkt_size =208
width =1280
height =720
pix_fmt = yuv420p
sample_aspect_ratio =1:1
pict_type = I
coded_picture_number =0
display_picture_number =0
interlaced_frame =0
top_field_first =0
repeat_pict =0
color_range = unknown
color_space = unknown
color_primaries = unknown
color_transfer = unknown
chroma_location = left
[/FRAME ]
[ FRAME ]
media_type = audio
stream_index =1
key_frame =1
pkt_pts =0
pkt_pts_time =0.000000
pkt_dts =0
pkt_dts_time =0.000000
best_effort_timestamp = -7
best_effort_timestamp_time = -0.007000
pkt_duration =13
pkt_duration_time =0.013000
pkt_pos =1368
pkt_size =3
sample_fmt = fltp
nb_samples =648
channels =2
channel_layout = stereo
[/ FRAME ]

Notice that this snippet contains two frames, one video and one audio, each
with a set of media-specific fields. Collectively, we’re left with the follow-
ing collection of fields: best effort timestamp, best effort timestamp time, chan-
nel layout, channels, chroma location, coded picture number, color primaries,
color range, color space, color transfer, display picture number, height, inter-
laced frame, key frame, media type, nb samples, pict type, pix fmt, pkt dts, pkt dts time,
pkt duration, pkt duration time, pkt pos, pkt pts, pkt pts time, pkt size, repeat pict,
sample aspect ratio, sample fmt, stream index, top field first, width.

A dilligent and motivated reader could spend time investigating each field, but I’m more of a pass/fail kinda guy so we’ll limit our interest in a few relevant fields and briefly discuss the relevance of others.
Each packet specifies a media type, audio or video. Let’s focus on video frames for now, we can select only video streams to simplify our review.

$ ffprobe - loglevel quiet - select_streams V -
show_frames BigBuckBunny . mp4[ FRAME ]
media_type = video
stream_index =0
key_frame =1
pkt_pts =0
pkt_pts_time =0.000000
pkt_dts =0
pkt_dts_time =0.000000
best_effort_timestamp =0
best_effort_timestamp_time =0.000000
pkt_duration =41
pkt_durat ion_ti me =0.041000
pkt_pos =1111
pkt_size =208
width =1280
height =720
pix_fmt = yuv420p
sample_aspect_ratio =1:1
pict_type = I
coded_picture_number =0
display_picture_number =0
interlaced_frame =0
top_field_first =0
repeat_pict =0
color_range = unknown
color_space = unknown
color_primaries = unknown
color_transfer = unknown
chroma_location = left
[/ FRAME ]
[ FRAME ]
media_type = video
stream_index =0
key_frame =0
pkt_pts =42
pkt_pts_time =0.042000
pkt_dts =42
pkt_dts_time =0.042000
best_effort_timestamp =42
best_effort_timestamp_time =0.042000
pkt_duration =41
pkt_duration_time =0.041000
pkt_pos =1325
pkt_size =37
width =1280
height =720pix_fmt = yuv420p
sample_aspect_ratio =1:1
pict_type = P

Packet Fields You’ll notice there are a number of packet-wise fields (8 specifically), remember even though we are inspecting a file many video/audio protocols support streaming and are more relevant for such purposes. Despite being named with packet-prefix/suffix, they are often relevant for files as well, so don’t simply disregard.

PictureType Field The pict type field can be particularly interesting for those interested in video compression. Video picture types are often referred to as I-frames, B-frames, or P-frames; each having to do with video compression. I-frames are modestly compressed and are considered self-contained, not requiring other frames to decode. P-frames and B-frames (bi-directional) however employ a higher level of compression by capitalizing on similarity with the previous or following frames. For example, rather than compress an entire video frame, if two video frames are similar differing slightly in specific regions we can focus our compression/storage to the regions of change and greatly increase our com-
pression as a result. That’s precisely the relevance of P-frames and B-frames. P-frames use data from the previous frame, storing/compressing what’s different rather than the entire frame. B-frames extend on this by utilizing the previous frame and the following frame. Pretty neat, huh?

Timestamp Fields Two particularly interesting fields are decoding time stamp (DTS) and presentation time stamp (PTS). These timestamps are particularly interesting when you wish to modify the playback speed of a video. The presentation time stamp (PTS) indicates at what respective time the frame should
be ’presented’, or displayed. At 3 minutes, 30.01 seconds into the movie, what frame(s) should pop up for the viewer? Adjusting the PTS of a file therefore can shift, speed up/down or simply alter when the frame is presented. Halving the PTS will speed up a video, doubling the PTS will slow it down. Relatively simple.

The decoding time stamp (DTS) however is often identical (or similar) to the PTS, but not necessarily. Why would we possibly need yet another timestamp? It all comes back to compression, let’s say a sequence of video frames come in the form of I-frames, P-frames and B-frames: I P B B... The I-frame is self contained, the following P-frame (which is dependent on the previous frame) can rely on the previous frame being decompressed before hand (because the previous frame PTS ¡ current frame PTS), but B-frames throw a wrench into the mix. B-frames are reliant on the previous and the next frame, so both those frames must be decompressed before the B-frame can be decompressed. As a general rule, PTS and DTS time stamps tend to only differ when a stream has B-frames in it. The first 30 frames, roughly the first second of our video, are aseries of I,P,B frames.

$ ffprobe -loglevel quiet -select_streams V -show_frames -show_entries frame=pict_type BigBuckBunny.mp4
[ FRAME ]
pict_type = I
[/ FRAME ]
[ FRAME ]
pict_type = P
[/ FRAME ]
[ FRAME ]
pict_type = P
[/ FRAME ]
[ FRAME ]
pict_type = P
[/ FRAME ]
[ FRAME ]
pict_type = P
[/ FRAME ]
[ FRAME ]
pict_type = B
[/ FRAME ]
[ FRAME ]
pict_type = B
[/ FRAME ]
[ FRAME ]
pict_type = P
[/ FRAME ]
[ FRAME ]
pict_type = B
[/ FRAME ]
[ FRAME ]
pict_type = P
[/ FRAME ]

Lovely, right?

Another pair of timestamps, a bit less relevant, are ’best effort’ timestamps (best effort timestamp,best effort timestamp time). These tend to only be relevant for streams that only specify a DTS timestamp (e.g. no PTS). Literally attempting to provide a guess for a PTS-like value, enforcing a monotonicly
increasing timestamp) derived from available timestamp values. 

Video Size Fields So riddle me this, why does each video stream have width/height fields? Wouldn’t that be better suited in the container? One, uniformly sized video file, right? Nope. A container often contains a number of audio tracks (alternative languages, director commentary,...), similarly a number of subtitles for a variety of languages. While not overly-common, a container can providemultiple video streams as well, alternative angles, 360-degree video, picture-in-picture.... Each video stream therefore requires an independent sizing fields to properly display.

So, that's it, that's all I got for now.  I feel like I better understand some of the fields, specifically DTS/PTS and a clearer understanding of the various compression frames.  Hope it was equally useful to you.

Cheers.

Thursday, March 31, 2022

FFmpeg - Concatenating Videos with Blend Transition


 

I received an anonymous comment on a past post FFMpeg Blending specifically asking how two video files could be concatenated with a blending effect transition.  

Note: anonymous commenter/reader, if you find yourself returning and find this post useful please drop a quick comment so I know the effort in addressing your question wasn't a lost. Thanks!

In past posts I feel we've covered most of the necessary filtering examples as well as concatenation of videos, it's a not-so-simple matter of putting them all together.  Let's start with constraints/complications that can bite you.


Constraints/Complications

Input Files

If your input files come from a uniform source (e.g. your phone, video camera, snippets from a larger video file) you may not have to worry about these factors.  For this example however, we will grab a couple videos from YouTube from different sources so we will have to address some issues.  

In general, your input files need to be similarly formatted.  Similar frame rates, video resolutions and audio tracks.  I've wasted more hours than I care to mention forgetting to take care in this step, let that be a word of caution to emphasize take a few minutes to check this beforehand to avoid making the mistakes I've made.  Best-case, if your formats don't match you get slapped in the face with an error from FFmpeg, worse case it succeeds in delivering a video that looks like garbage leaving you with an Easter Egg Hunt in finding the issue.  When in doubt, enforce some standard format prior to doing anything more sophisticated.

Let's start with video resolution; your input files (for concatenation and blends) need to be identically sized, not kinda similarly sized, but identically sized.  Source files that have different aspect ratios are particularly problematic because resizing them may not give you the required sizing leaving you to crop/pad accordingly.  

A consistent frame rate (FPS) is necessary for time-based filters, like the blending, make sure your frame rate (FPS) is identical for both input videos, otherwise you'll encounter unexpected results that may resemble this;

A consistent auto track existence; just shy of a bazillion times I've attempted to concatenate two videos only to find my audio tracks absent/stalling.  For example, applying an image snippet before/after a video results in stalling or absent audio until I relearn that I need to apply an empty audio track to the image video, otherwise concatenation doesn't know how to properly handle concatenation of one video with audio and another without.  All your source media for concatenation should all have an audio track or none have an audio tracks.  Mixing and matching will only give you headaches in the end.


Example

Let's move on to our example.

Let's snag two videos from YouTube

$ youtube-dl -f mp4 -o yt-stingray.mp4 https://www.youtube.com/watch?v=aXTk9VPZ4Gg


$  youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4

Examining these files, you'll find they are aren't uniform in resolution or framerate.
$ ffprobe -i yt-stingray.mp4 
ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers
  built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 20160609
  configuration: --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmq
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yt-stingray.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2019-05-22T15:56:25.000000Z
  Duration: 00:47:09.44, start: 0.000000, bitrate: 397 kb/s
    Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, smpte170m), 480x360 [SAR 1:1 DAR 4:3], 299 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
    Metadata:
      creation_time   : 2019-05-22T15:56:25.000000Z
      handler_name    : ISO Media file produced by Google Inc. Created on: 05/22/2019.
    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 95 kb/s (default)
    Metadata:
      creation_time   : 2019-05-22T15:56:25.000000Z
      handler_name    : ISO Media file produced by Google Inc. Created on: 05/22/2019.

$ ffprobe -i yt-hardcastle01.mp4 
ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers
  built with gcc 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 20160609
  configuration: --enable-libx264 --enable-nonfree --enable-gpl --enable-libfreetype --enable-libmp3lame --enable-libzmq
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'yt-hardcastle01.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    encoder         : Google
  Duration: 00:01:48.62, start: 0.000000, bitrate: 1362 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720 [SAR 1:1 DAR 16:9], 1232 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      handler_name    : ISO Media file produced by Google Inc.
    Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 127 kb/s (default)
    Metadata:
      handler_name    : ISO Media file produced by Google Inc.

Concatenation 

Even with differing video resolutions and framerates, you can find reasonable success with simple concatenating of the video files.  FFmpeg will adopt the video resolution and framerate of the 1st input file and apply it throughout the output file.  Swapping the order of the above files results in the end resolution of the output file.
$ cat videoXX.mp4.txt
file 'yt-stingray.mp4'
file 'yt-hardcastle01.mp4'

$ ffmpeg -y -f concat -i videoXX.mp4.txt videoXX.mp4

This concatenation order results in 480x360 23.98 fps, while reversing the file order results in 1280x720 29.97 fps; often leaving you scratch your head.

Sometimes this will give you the result you're looking for, other times it may not.  I find it's in my best interest to do the scaling prior to concatenation, cropping and/or padding as I prefer.  Additionally, becoming overly reliant on FFmpeg 'doing it for you' avoids understanding what's going on only to bite you in the butt when you try something more complicated (e.g. like blending).


Blending

The blending effect we will be utilizing is a pixel-wide operations, one-by-one each frame is generated by 'blending' the corresponding pixels from each input file into the pixel in the destination frame.  If the two input media aren't identically scaled the blending falls apart because it lacks corresponding pixels.  FFmpeg errors out with an error resembling this;

[Parsed_amerge_0 @ 0x2e8b1c0] No channel layout for input 1
[Parsed_amerge_0 @ 0x2e8b1c0] Input channel layouts overlap: output layout will be determined by the number of distinct input channels
[Parsed_blend_2 @ 0x2dc7c80] First input link top parameters (size 480x360) do not match the corresponding second input link bottom parameters (size 1280x720)
[Parsed_blend_2 @ 0x2dc7c80] Failed to configure output pad on Parsed_blend_2
Error reinitializing filters!
Failed to inject frame into filter network: Invalid argument
Error while processing the decoded data for stream #1:0
Conversion failed!

This is FFmpegs way of saying that your input videos aren't uniformly sized.

Scaling input files with a variety of aspect ratios is always challenging, I find for me scaling to a specific video size w/optional padding seems to work well for me.

Let's scale our input videos to 1280x720, applying padding if necessary, and force a uniform 30/1 frame rate;

$ ffmpeg -i yt-stingray.mp4 -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -r 30/1-strict -2 yt-stingray-scaled.mp4
$ ffmpeg -i yt-hardcastle.mp4 -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -r 30/1 -strict -2 yt-hardcastle-scaled.mp4

We are left with uniformly 1280x720 30/1 fps videos that can be blended.

Before we demonstrate the use of the blending filter, simple blending 'generally' begins being applied right away, not quite what we want if we want to play the first video until near completion, then blend into the next video.  While you absolutely can do it in a single sequence of commands, it would require adjusting the 1st video presentation time stamp (PTS) to start immediately, adjust the 2nd video PTS to nearly the duration of video1 (play this, then this) and then apply blending at the end of video1.  Personally, that's a recipe for a migraine, so an alternative is to break the two videos into segments and stitch them back together, so that's the tactic we'll use in our example.

Since these video source files are so large, let's shrink them down a bit for our example.  Let's grab 10 sec intervals to work with as they'll be processed quicker and a bit easier to understand.

$ ffmpeg -i yt-stingray-scaled.mp4 -ss 4 -t 10 -strict -2 clip01-scaled.mp4
$ ffmpeg -i yt-hardcastle-scaled.mp4 -ss 4 -t 10 -strict -2 clip02-scaled.mp4

So, we are looking for an end effect of playing the first 8 seconds of clip01-scaled.mp4, then apply a 2-sec blending transition, then continue to play video clip02-scaled.mp4.  That's our bogey.

We can achieve this by splitting the videos into 4 segments;
  • segment1.mp4 : first 8 seconds of clip01-scaled.mp4
  • segment2.mp4 : last 2 seconds of clip01-scaled.mp4
  • segment3.mp4 : first 2 seconds of clip02-scaled.mp4
  • segment4.mp4 : last 8 seconds of clip02-scaled.mp4
With these 4 segments, we will apply the blending transition to segment2 & segment3, then restitch the blended clip between segment1 and segment4.  That's the plan.  Let's walk thru that.

$ ffmpeg -i clip01-scaled.mp4 -t 8.032000 -target ntsc-dvd -strict -2 segment1.mp4
$ ffmpeg -i clip01-scaled.mp4 -ss 8.032000 -target ntsc-dvd -strict -2 segment2.mp4
$ ffmpeg -i clip02-scaled.mp4 -ss 0 -t 2 -target ntsc-dvd -strict -2 segment3.mp4
$ ffmpeg -i clip02-scaled.mp4 -ss 2 -target ntsc-dvd -strict -2 segment4.mp4

Let's blend segment2 and segment3; at the beginning of the output file we are attempting to utilize video1 frame blend factor of 100%, video2 frame of 0%, at the end of the clip reversing to 0% video1, 100% video2.  A time-based linear progression over the duration of the video clip.

$ ffmpeg -i segment2.mp4 -i segment3.mp4 -filter_complex "[0:v]setpts=PTS-STARTPTS[v0],[1:v]setpts=PTS-STARTPTS[v1],[v0][v1]blend=all_expr='A*(1-(T/2))+B*(T/2)'" -filter_complex "amerge=inputs=2" -ac 2 -shortest -target ntsc-dvd segment2a.mp4

It's possible that the input files have a non-zero PTS start time, which is later used (as time variable T), so we're forcing both video file PTS values to start at zero.  That way, the timestamp from both videos is uniform.  The blend filter A*factor1+B*factor2, each factor in the range of [0,1].  The amerge command similarly attempts to blend both audio tracks together.

With a clip duration of 2 seconds, T/2 is a linear progression from 0 to 1 working well for our 2nd video blending factor.  Wishing a decreasing blending factor for the 1st video (1-(T/2)) works.  Pay specific attention to the video duration component (e.g. 2), it needs to align with the length of the videos, otherwise the blending factors will exceed [0,1] and give you an effect that can only compare to dropping acid.  The ''-shortest' option may, or may not, be necessary, your mileage may vary.  I've added because without it sometimes I observe a slight delay in the re-assembled video.

Reassembling Video Clips

Let's reassemble our intro, outtro and blended clips into our final video.

$ cat video.mp4.txt 
file 'segment1.mp4'
file 'segment2a.mp4'
file 'segment4.mp4'

$ ffmpeg -y -f concat -i video.mp4.txt video.mp4

We're left with our final video.




The full Makefile, for those interested in re-creating a little less manually and may ease applying to your own source files.  Notice the ffprobe commands are used to automate the extraction of video durations, the TransitionDuration=2 implies a 2-second blending duration between the two videos.

$ cat -n Makefile 
     1 TransitionDuration=2
     2 all: video.mp4
     3
     4 segment1.mp4 : clip01-scaled.mp4
     5 ${SH} ffmpeg -i $< -t $(shell echo $(shell ffprobe -loglevel quiet -show_format $< -show_entries format=duration | grep duration | cut -f 2 -d '=') -${TransitionDuration} | bc) -target ntsc-dvd -strict -2 $@
     6
     7 segment2.mp4 : clip01-scaled.mp4
     8 ${SH} ffmpeg -i $< -ss $(shell echo $(shell ffprobe -loglevel quiet -show_format $< -show_entries format=duration | grep duration | cut -f 2 -d '=') -${TransitionDuration} | bc) -target ntsc-dvd -strict -2 $@
     9
    10 segment3.mp4 : clip02-scaled.mp4
    11 ${SH} ffmpeg -i $< -ss 0 -t $(shell echo ${TransitionDuration} -1 | bc) -target ntsc-dvd -strict -2 $@
    12
    13 segment4.mp4 : clip02-scaled.mp4
    14 ${SH} ffmpeg -i $< -ss ${TransitionDuration} -target ntsc-dvd -strict -2 $@
    15
    16 segment2a.mp4 : segment2.mp4 segment3.mp4
    17 ${SH} ffmpeg -i $(shell echo $^ | cut -f 1 -d ' ') -i $(shell echo $^ | cut -f 2 -d ' ') -filter_complex "[0:v]setpts=PTS-STARTPTS[v0],[1:v]setpts=PTS-STARTPTS[v1],[v0][v1]blend=all_expr='A*(1-(T/${TransitionDuration}))+B*(T/${TransitionDuration})'" -filter_complex "amerge=inputs=2" -ac 2 -shortest -target ntsc-dvd $@
    18
    19 video.mp4: segment1.mp4 segment2a.mp4 segment4.mp4
    20 ${SH} rm $@.txt | true
    21 ${SH} for f in $^; do echo "file '$$f'" >> $@.txt; done
    22 ${SH} ffmpeg -y -f concat -i $@.txt $@
    23 ${SH} mplayer $@
    24
    25 clip01.mp4: yt-stingray.mp4
    26 ${SH} ffmpeg -i $< -ss 4 -t 10 -strict -2 $@
    27
    28 clip02.mp4: yt-hardcastle01.mp4
    29 ${SH} ffmpeg -i $< -ss 9 -t 10 -strict -2 $@
    30
    31 yt-stingray.mp4:
    32 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg
    33
    34 yt-hardcastle01.mp4:
    35 ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4
    36
    37 %-scaled.mp4: %.mp4
    38 ${SH} ffmpeg -i $< -vf "scale=-1:720,pad=1280:ih:(ow-iw)/2" -strict -2 $@
    39
    40
    41 clean:
    42 ${RM} *.mp4


Cheers.




Monday, March 21, 2022

Concatenating Non-Uniformly Scaled Videos w/FFmpeg

 Big hair, mullets and acid-washed jeans...the 1980's introduced us to a number of iconic treasures.  But, for me, and many young teenage boys I yearned for the iconic "throaty V8's" that took center stage during the nightly broadcasts.  My hand-painted Schwinn was magically transformed by imagination as we cruised around the neighborhood.


With that memory in mind, I collected a number of these marvelous machines (in a variety of formats/resolutions) and using the magic of FFmpeg created a video.

In past posts I've cheered the use of 'makefiles' with FFmpeg, believing they are a great match for one another; Atypical Uses for Makefiles


A series of videos are first downloaded from YouTube, sectioned into short clips (where the automobile is taking center stage), stitching them together, re-adding a soundtrack until we have N videos, one for each TV series.  Then, these videos are stitched together while applying a uniform scaling giving us one final video with them all.

While this could be accomplished with a single, monstrous Makefile, I felt it was easier to organize each tv series into it's own makefile (e.g. makefile.stingray), each included into the organizing makefile (e.g. Makefile).

 

  cat -n Makefile
     1    ClipW=1280
     2    ClipH=720
     3    Fps=30/1
     4    include makefile.stingray
     5    include makefile.miamivice
     6    include makefile.hardcastle
     7    include makefile.ateam
     8    include makefile.fallguy
     9    include makefile.knightrider
    10    include makefile.airwolf
    11    include makefile.dukes
    12    include makefile.magnum
    13   
    14    all: video.mp4
    15   
    16    #video.mp4: stingray-scaled.mp4 miamiVice-scaled.mp4 hardcastle-scaled.mp4 aTeam-scaled.mp4 fallGuy-scaled.mp4 knightRider-scaled.mp4 airwolf-scaled.mp4 dukes-scaled.mp4 magnum-scaled.mp4
    17    video.mp4: stingray-scaled.mp4 miamiVice-scaled.mp4 hardcastle-scaled.mp4 aTeam-scaled.mp4 fallGuy-scaled.mp4 knightRider-scaled.mp4 airwolf-scaled.mp4 dukes-scaled.mp4 magnum-scaled.mp4
    18        ${SH} rm $@.txt | true
    19        ${SH} for f in $^; do echo "file '$$f'" >> $@.txt; done
    20        ${SH} ffmpeg -y -f concat -i $@.txt $@
    21        ${SH} mplayer $@
    22   
    23   
    24    go: stingray-scaled.mp4
    25   
    26    %-scaled.mp4: %.mp4
    27    #    ${SH} ffmpeg -y -i $< -filter_complex "scale=${ClipW}:-2,scale=-2:${ClipH},pad=${ClipW}:${ClipH}:(ow-iw)/2:(oh-ih)/2" -r ${Fps} -acodec copy $@
    28        ${SH} ffmpeg -y -i $< -filter_complex "scale=${ClipW}:-2,scale=-2:${ClipH},scale=${ClipW}:${ClipH},pad=${ClipW}:${ClipH}:(ow-iw)/2:(oh-ih)/2" -r ${Fps} -acodec copy $@
    29   
    30    littleClean:
    31        ${SH} find . -name "*.mp4" ! -name 'yt-*.mp4' -delete
    32        ${RM} *.mp3
    33        ${RM} *.txt
    34   
Pay special attention to the 'scaled' target, this can take a variety of video resolutions (equal-to or smaller-than 1280x720) and scales them to 1280x720 w/padding.  It's always a hassle wrangling videos into a uniform size so they can be concatenated, these makefile targets do precisely that.

In an attempt to be thorough, I'll slap in the subordinate makefiles below, each snags a youtube video(s), creates a clip, extracts the theme song, concatenates the clips together and re-applys the audio channel.

$ cat -n makefile.stingray
     1    all:
     2   
     3    yt-stingray.mp4:
     4        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg
     5   
     6    stingray.mp3: yt-stingray.mp4
     7        ${SH} ffmpeg -i $< -ss 35 -t 85 -vn -strict -2 $@
     8   
     9    sr-clip01.mp4: yt-stingray.mp4
    10        ${SH} ffmpeg -i $< -ss 4 -t 2 -strict -2 $@
    11   
    12    sr-clip02.mp4: yt-stingray.mp4
    13        ${SH} ffmpeg -i $< -ss 26 -t 1.0 -strict -2 $@
    14   
    15    sr-clip03.mp4: yt-stingray.mp4
    16        ${SH} ffmpeg -i $< -ss 33.30 -t 1.0 -strict -2 $@
    17   
    18    sr-clip04.mp4: yt-stingray.mp4
    19        ${SH} ffmpeg -i $< -ss 37.00 -t 16.0 -strict -2 $@
    20   
    21    sr-clip05.mp4: yt-stingray.mp4
    22        ${SH} ffmpeg -i $< -ss 61.50 -t 1.0 -strict -2 $@
    23   
    24    sr-clip06.mp4: yt-stingray.mp4
    25        ${SH} ffmpeg -i $< -ss 1643.00 -t 6.0 -strict -2 $@
    26   
    27    sr-clip07.mp4: yt-stingray.mp4
    28        ${SH} ffmpeg -i $< -ss 1874.50 -t 3.0 -strict -2 $@
    29   
    30    sr-clip08.mp4: yt-stingray.mp4
    31        ${SH} ffmpeg -i $< -ss 2083.00 -t 6.0 -strict -2 $@
    32   
    33    sr-clip09.mp4: yt-stingray.mp4
    34        ${SH} ffmpeg -i $< -ss 2126.50 -t 10.5 -strict -2 $@
    35   
    36    sr-clip10.mp4: yt-stingray.mp4
    37        ${SH} ffmpeg -i $< -ss 2142.00 -t 2.0 -strict -2 $@
    38   
    39    sr-clip11.mp4: yt-stingray.mp4
    40        ${SH} ffmpeg -i $< -ss 2149.00 -t 6.0 -strict -2 $@
    41   
    42    sr-clip12.mp4: yt-stingray.mp4
    43        ${SH} ffmpeg -i $< -ss 2243.00 -t 1.6 -strict -2 $@
    44   
    45    sr-clip13.mp4: yt-stingray.mp4
    46        ${SH} ffmpeg -i $< -ss 2246.00 -t 12.0 -strict -2 $@
    47   
    48    sr-clip14.mp4: yt-stingray.mp4
    49        ${SH} ffmpeg -i $< -ss 2260.00 -t 10.0 -strict -2 $@
    50   
    51    sr-clip15.mp4: yt-stingray.mp4
    52        ${SH} ffmpeg -i $< -ss 2278.75 -t 1.0 -strict -2 $@
    53   
    54    sr-clip16.mp4: yt-stingray.mp4
    55        ${SH} ffmpeg -i $< -ss 2282.0 -t 2.5 -strict -2 $@
    56   
    57    sr-clip17.mp4: yt-stingray.mp4
    58        ${SH} ffmpeg -i $< -ss 2285.5 -t 1.0 -strict -2 $@
    59   
    60    sr-clip18.mp4: yt-stingray.mp4
    61        ${SH} ffmpeg -i $< -ss 2290.5 -t 0.25 -strict -2 $@
    62   
    63    sr-clip19.mp4: yt-stingray.mp4
    64        ${SH} ffmpeg -i $< -ss 2293.0 -t 0.50 -strict -2 $@
    65   
    66    sr-clip20.mp4: yt-stingray.mp4
    67        ${SH} ffmpeg -i $< -ss 2298.0 -t 1.00 -strict -2 $@
    68   
    69    sr-clip21.mp4: yt-stingray.mp4
    70        ${SH} ffmpeg -i $< -ss 2304.0 -t 3.00 -strict -2 $@
    71   
    72    sr-clip22.mp4: yt-stingray.mp4
    73        ${SH} ffmpeg -i $< -ss 2310.0 -t 2.00 -strict -2 $@
    74   
    75    sr-clip23.mp4: yt-stingray.mp4
    76        ${SH} ffmpeg -i $< -ss 2317.0 -t 4.00 -strict -2 $@
    77   
    78    sr-clip24.mp4: yt-stingray.mp4
    79        ${SH} ffmpeg -i $< -ss 2325.0 -t 2.00 -strict -2 $@
    80   
    81    sr-clip25.mp4: yt-stingray.mp4
    82        ${SH} ffmpeg -i $< -ss 2328.5 -t 2.00 -strict -2 $@
    83   
    84    sr-clip26.mp4: yt-stingray.mp4
    85        ${SH} ffmpeg -i $< -ss 2332.0 -t 0.50 -strict -2 $@
    86   
    87    sr-clip27.mp4: yt-stingray.mp4
    88        ${SH} ffmpeg -i $< -ss 2334.0 -t 1.00 -strict -2 $@
    89   
    90    sr-clip28.mp4: yt-stingray.mp4
    91        ${SH} ffmpeg -i $< -ss 2336.0 -t 1.00 -strict -2 $@
    92   
    93    sr-clip29.mp4: yt-stingray.mp4
    94        ${SH} ffmpeg -i $< -ss 2354.0 -t 1.50 -strict -2 $@
    95   
    96    sr-clip30.mp4: yt-stingray.mp4
    97        ${SH} ffmpeg -i $< -ss 2361.0 -t 14.00 -strict -2 $@
    98   
    99    sr-videoAn.mp4: sr-clip01.mp4 sr-clip04.mp4 sr-clip02.mp4 sr-clip03.mp4 sr-clip08.mp4 sr-clip09.mp4 sr-clip11.mp4 sr-clip12.mp4 sr-clip13.mp4 sr-clip15.mp4 sr-clip16.mp4 sr-clip17.mp4 sr-clip18.mp4 sr-clip19.mp4 sr-clip20.mp4 sr-clip22.mp4 sr-clip24.mp4 sr-clip27.mp4 sr-clip28.mp4 sr-clip29.mp4 sr-clip30.mp4
   100        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
   101        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
   102        ${RM} $@.txt
   103   
   104    stingray.mp4: sr-videoAn.mp4 stingray.mp3
   105        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
   106   
   107    clean:
   108        ${RM} sr*.mp4
$ cat -n makefile.stingray
     1    all:
     2   
     3    yt-stingray.mp4:
     4        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=aXTk9VPZ4Gg
     5   
     6    stingray.mp3: yt-stingray.mp4
     7        ${SH} ffmpeg -i $< -ss 35 -t 85 -vn -strict -2 $@
     8   
     9    sr-clip01.mp4: yt-stingray.mp4
    10        ${SH} ffmpeg -i $< -ss 4 -t 2 -strict -2 $@
    11   
    12    sr-clip02.mp4: yt-stingray.mp4
    13        ${SH} ffmpeg -i $< -ss 26 -t 1.0 -strict -2 $@
    14   
    15    sr-clip03.mp4: yt-stingray.mp4
    16        ${SH} ffmpeg -i $< -ss 33.30 -t 1.0 -strict -2 $@
    17   
    18    sr-clip04.mp4: yt-stingray.mp4
    19        ${SH} ffmpeg -i $< -ss 37.00 -t 16.0 -strict -2 $@
    20   
    21    sr-clip05.mp4: yt-stingray.mp4
    22        ${SH} ffmpeg -i $< -ss 61.50 -t 1.0 -strict -2 $@
    23   
    24    sr-clip06.mp4: yt-stingray.mp4
    25        ${SH} ffmpeg -i $< -ss 1643.00 -t 6.0 -strict -2 $@
    26   
    27    sr-clip07.mp4: yt-stingray.mp4
    28        ${SH} ffmpeg -i $< -ss 1874.50 -t 3.0 -strict -2 $@
    29   
    30    sr-clip08.mp4: yt-stingray.mp4
    31        ${SH} ffmpeg -i $< -ss 2083.00 -t 6.0 -strict -2 $@
    32   
    33    sr-clip09.mp4: yt-stingray.mp4
    34        ${SH} ffmpeg -i $< -ss 2126.50 -t 10.5 -strict -2 $@
    35   
    36    sr-clip10.mp4: yt-stingray.mp4
    37        ${SH} ffmpeg -i $< -ss 2142.00 -t 2.0 -strict -2 $@
    38   
    39    sr-clip11.mp4: yt-stingray.mp4
    40        ${SH} ffmpeg -i $< -ss 2149.00 -t 6.0 -strict -2 $@
    41   
    42    sr-clip12.mp4: yt-stingray.mp4
    43        ${SH} ffmpeg -i $< -ss 2243.00 -t 1.6 -strict -2 $@
    44   
    45    sr-clip13.mp4: yt-stingray.mp4
    46        ${SH} ffmpeg -i $< -ss 2246.00 -t 12.0 -strict -2 $@
    47   
    48    sr-clip14.mp4: yt-stingray.mp4
    49        ${SH} ffmpeg -i $< -ss 2260.00 -t 10.0 -strict -2 $@
    50   
    51    sr-clip15.mp4: yt-stingray.mp4
    52        ${SH} ffmpeg -i $< -ss 2278.75 -t 1.0 -strict -2 $@
    53   
    54    sr-clip16.mp4: yt-stingray.mp4
    55        ${SH} ffmpeg -i $< -ss 2282.0 -t 2.5 -strict -2 $@
    56   
    57    sr-clip17.mp4: yt-stingray.mp4
    58        ${SH} ffmpeg -i $< -ss 2285.5 -t 1.0 -strict -2 $@
    59   
    60    sr-clip18.mp4: yt-stingray.mp4
    61        ${SH} ffmpeg -i $< -ss 2290.5 -t 0.25 -strict -2 $@
    62   
    63    sr-clip19.mp4: yt-stingray.mp4
    64        ${SH} ffmpeg -i $< -ss 2293.0 -t 0.50 -strict -2 $@
    65   
    66    sr-clip20.mp4: yt-stingray.mp4
    67        ${SH} ffmpeg -i $< -ss 2298.0 -t 1.00 -strict -2 $@
    68   
    69    sr-clip21.mp4: yt-stingray.mp4
    70        ${SH} ffmpeg -i $< -ss 2304.0 -t 3.00 -strict -2 $@
    71   
    72    sr-clip22.mp4: yt-stingray.mp4
    73        ${SH} ffmpeg -i $< -ss 2310.0 -t 2.00 -strict -2 $@
    74   
    75    sr-clip23.mp4: yt-stingray.mp4
    76        ${SH} ffmpeg -i $< -ss 2317.0 -t 4.00 -strict -2 $@
    77   
    78    sr-clip24.mp4: yt-stingray.mp4
    79        ${SH} ffmpeg -i $< -ss 2325.0 -t 2.00 -strict -2 $@
    80   
    81    sr-clip25.mp4: yt-stingray.mp4
    82        ${SH} ffmpeg -i $< -ss 2328.5 -t 2.00 -strict -2 $@
    83   
    84    sr-clip26.mp4: yt-stingray.mp4
    85        ${SH} ffmpeg -i $< -ss 2332.0 -t 0.50 -strict -2 $@
    86   
    87    sr-clip27.mp4: yt-stingray.mp4
    88        ${SH} ffmpeg -i $< -ss 2334.0 -t 1.00 -strict -2 $@
    89   
    90    sr-clip28.mp4: yt-stingray.mp4
    91        ${SH} ffmpeg -i $< -ss 2336.0 -t 1.00 -strict -2 $@
    92   
    93    sr-clip29.mp4: yt-stingray.mp4
    94        ${SH} ffmpeg -i $< -ss 2354.0 -t 1.50 -strict -2 $@
    95   
    96    sr-clip30.mp4: yt-stingray.mp4
    97        ${SH} ffmpeg -i $< -ss 2361.0 -t 14.00 -strict -2 $@
    98   
    99    sr-videoAn.mp4: sr-clip01.mp4 sr-clip04.mp4 sr-clip02.mp4 sr-clip03.mp4 sr-clip08.mp4 sr-clip09.mp4 sr-clip11.mp4 sr-clip12.mp4 sr-clip13.mp4 sr-clip15.mp4 sr-clip16.mp4 sr-clip17.mp4 sr-clip18.mp4 sr-clip19.mp4 sr-clip20.mp4 sr-clip22.mp4 sr-clip24.mp4 sr-clip27.mp4 sr-clip28.mp4 sr-clip29.mp4 sr-clip30.mp4
   100        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
   101        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
   102        ${RM} $@.txt
   103   
   104    stingray.mp4: sr-videoAn.mp4 stingray.mp3
   105        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
   106   
   107    clean:
   108        ${RM} sr*.mp4
$ cat -n makefile.miamivice
     1    yt-miamiVice01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=JmFyFqTJprg
     3   
     4    yt-miamiVice02.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=dEjXPY9jOx8
     6   
     7    miamiVice.mp3: yt-miamiVice02.mp4
     8        ${SH} ffmpeg -i $< -vn $@
     9   
    10    #56-78
    11    mv-clip01.mp4: yt-miamiVice01.mp4
    12        ${SH} ffmpeg -i $< -ss 56 -t 22 $@
    13        ${SH} mplayer $@
    14   
    15    #84.5-87
    16    mv-clip02.mp4: yt-miamiVice01.mp4
    17        ${SH} ffmpeg -i $< -ss 84.5 -t 2.5 $@
    18        ${SH} mplayer $@
    19   
    20    #114-118
    21    mv-clip03.mp4: yt-miamiVice01.mp4
    22        ${SH} ffmpeg -i $< -ss 114 -t 4 $@
    23        ${SH} mplayer $@
    24   
    25    #122-127
    26    mv-clip04.mp4: yt-miamiVice01.mp4
    27        ${SH} ffmpeg -i $< -ss 122 -t 5 $@
    28        ${SH} mplayer $@
    29   
    30    #131-142
    31    mv-clip05.mp4: yt-miamiVice01.mp4
    32        ${SH} ffmpeg -i $< -ss 131 -t 11 $@
    33        ${SH} mplayer $@
    34   
    35    #153-160
    36    mv-clip06.mp4: yt-miamiVice01.mp4
    37        ${SH} ffmpeg -i $< -ss 154.5 -t 7 $@
    38        ${SH} mplayer $@
    39   
    40    #169-174
    41    mv-clip07.mp4: yt-miamiVice01.mp4
    42        ${SH} ffmpeg -i $< -ss 169 -t 5 $@
    43        ${SH} mplayer $@
    44   
    45    #183-190
    46    mv-clip08.mp4: yt-miamiVice01.mp4
    47        ${SH} ffmpeg -i $< -ss 183 -t 7 $@
    48        ${SH} mplayer $@
    49   
    50    #193-194
    51    mv-clip09.mp4: yt-miamiVice01.mp4
    52        ${SH} ffmpeg -i $< -ss 193 -t 1 $@
    53        ${SH} mplayer $@
    54   
    55    #198-204
    56    mv-clip10.mp4: yt-miamiVice01.mp4
    57        ${SH} ffmpeg -i $< -ss 198 -t 5 $@
    58        ${SH} mplayer $@
    59   
    60    #254-265
    61    mv-clip11.mp4: yt-miamiVice01.mp4
    62        ${SH} ffmpeg -i $< -ss 254 -t 11 $@
    63        ${SH} mplayer $@
    64   
    65    mv-videoAn.mp4: mv-clip01.mp4 mv-clip04.mp4 mv-clip02.mp4 mv-clip03.mp4 mv-clip08.mp4 mv-clip09.mp4 mv-clip11.mp4
    66        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    67        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    68        ${RM} $@.txt
    69   
    70    miamiVice.mp4: mv-videoAn.mp4 miamiVice.mp3
    71        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    72   
    73   
$ cat -n makefile.hardcastle
     1    yt-hardcastle01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=_oHpWw7L3d4
     3   
     4   
     5    yt-hardcastle02.mp4:
     6        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=J2_G1CNaeFs
     7   
     8    hardcastle.mp3: yt-hardcastle01.mp4
     9        ${SH} ffmpeg -i $< -ss 8.3 -t 91.6 -vn -strict -2 $@
    10   
    11    hm-clip01.mp4: yt-hardcastle01.mp4
    12        ${SH} ffmpeg -i $< -ss 8.3 -t 16.00 -strict -2 $@
    13   
    14    hm-clip02.mp4: yt-hardcastle01.mp4
    15        ${SH} ffmpeg -i $< -ss 82.3 -t 0.5 -strict -2 $@
    16   
    17    hm-clip03.mp4: yt-hardcastle01.mp4
    18        ${SH} ffmpeg -i $< -ss 89.5 -t 12.0 -strict -2 $@
    19   
    20    hm-clip04.mp4: yt-hardcastle02.mp4
    21        ${SH} ffmpeg -i $< -ss 31.5 -t 3.5 -strict -2 $@
    22   
    23    hm-clip05.mp4: yt-hardcastle02.mp4
    24        ${SH} ffmpeg -i $< -ss 40.0 -t 3.0 -strict -2 $@
    25   
    26    hm-clip06.mp4: yt-hardcastle02.mp4
    27        ${SH} ffmpeg -i $< -ss 51.0 -t 2.0 -strict -2 $@
    28   
    29    hm-clip07.mp4: yt-hardcastle02.mp4
    30        ${SH} ffmpeg -i $< -ss 68.5 -t 7.0 -strict -2 $@
    31   
    32    hm-clip08.mp4: yt-hardcastle02.mp4
    33        ${SH} ffmpeg -i $< -ss 84.5 -t 25.5 -strict -2 $@
    34   
    35    hm-clip09.mp4: yt-hardcastle02.mp4
    36        ${SH} ffmpeg -i $< -ss 116.0 -t 8.0 -strict -2 $@
    37   
    38    hm-clip10.mp4: yt-hardcastle02.mp4
    39        ${SH} ffmpeg -i $< -ss 133.0 -t 5.0 -strict -2 $@
    40   
    41    hm-clip11.mp4: yt-hardcastle02.mp4
    42        ${SH} ffmpeg -i $< -ss 143.5 -t 5.0 -strict -2 $@
    43   
    44    hm-videoAn.mp4: hm-clip01.mp4 hm-clip02.mp4 hm-clip04.mp4 hm-clip05.mp4 hm-clip06.mp4 hm-clip07.mp4 hm-clip08.mp4 hm-clip09.mp4 hm-clip10.mp4 hm-clip11.mp4 hm-clip03.mp4
    45        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    46        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    47        ${RM} $@.txt
    48   
    49    hardcastle.mp4: hm-videoAn.mp4 hardcastle.mp3
    50        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    51   
    52   
    53   
$ cat -n makefile.ateam
     1    yt-aTeam00.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=wyz_2DEah4o
     3   
     4    yt-aTeam01.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=orxNlmQSMg8
     6   
     7    yt-aTeam02.mp4:
     8        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=nn5gY7LK8Mc
     9   
    10    yt-aTeam03.mp4:
    11        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=ZgUBogpF-M8
    12   
    13    yt-aTeam04.mp4:
    14        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=TP0LowFsi9Q
    15   
    16    yt-aTeam05.mp4:
    17        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=7BcPko1RYv0
    18   
    19    at-clip01.mp4: yt-aTeam01.mp4
    20        ${SH} ffmpeg -i $< -ss 0 -t 10 -strict -2 $@
    21   
    22    at-clip02.mp4: yt-aTeam01.mp4
    23        ${SH} ffmpeg -i $< -ss 13 -t 9 -strict -2 $@
    24   
    25    at-clip03.mp4: yt-aTeam02.mp4
    26        ${SH} ffmpeg -i $< -ss 1.3 -t 2.0 -strict -2 $@
    27   
    28    at-clip04.mp4: yt-aTeam02.mp4
    29        ${SH} ffmpeg -i $< -ss 5.5 -t 0.25 -strict -2 $@
    30   
    31    at-clip05.mp4: yt-aTeam02.mp4
    32        ${SH} ffmpeg -i $< -ss 9.5 -t 5 -strict -2 $@
    33   
    34    at-clip06.mp4: yt-aTeam02.mp4
    35        ${SH} ffmpeg -i $< -ss 30 -t 2.5 -strict -2 $@
    36   
    37    at-clip07.mp4: yt-aTeam02.mp4
    38        ${SH} ffmpeg -i $< -ss 34.5 -t 1 -strict -2 $@
    39   
    40    at-clip08.mp4: yt-aTeam02.mp4
    41        ${SH} ffmpeg -i $< -ss 40 -t 2 -strict -2 $@
    42   
    43    at-clip09.mp4: yt-aTeam02.mp4
    44        ${SH} ffmpeg -i $< -ss 50 -t 3.5 -strict -2 $@
    45   
    46    at-clip10.mp4: yt-aTeam02.mp4
    47        ${SH} ffmpeg -i $< -ss 64 -t 5 -strict -2 $@
    48   
    49    at-clip11.mp4: yt-aTeam02.mp4
    50        ${SH} ffmpeg -i $< -ss 75.5 -t 1.5 -strict -2 $@
    51   
    52    at-clip12.mp4: yt-aTeam02.mp4
    53        ${SH} ffmpeg -i $< -ss 79 -t 7 -strict -2 $@
    54   
    55    at-clip13.mp4: yt-aTeam03.mp4
    56        ${SH} ffmpeg -i $< -ss 311 -t 7 -vf scale=486:360 -r 25/1 -strict -2 $@
    57   
    58    at-clip14.mp4: yt-aTeam04.mp4
    59        ${SH} ffmpeg -i $< -ss 218 -t 3 -vf scale=486:360 -r 25/1 -strict -2 $@
    60   
    61    at-clip15.mp4: yt-aTeam04.mp4
    62        ${SH} ffmpeg -i $< -ss 224 -t 1 -vf scale=486:360 -r 25/1 -strict -2 $@
    63   
    64    at-clip16.mp4: yt-aTeam04.mp4
    65        ${SH} ffmpeg -i $< -ss 228 -t 2 -vf scale=486:360 -r 25/1 -strict -2 $@
    66   
    67    at-clip17.mp4: yt-aTeam04.mp4
    68        ${SH} ffmpeg -i $< -ss 238 -t 1 -vf scale=486:360 -r 25/1 -strict -2 $@
    69   
    70    at-clip18.mp4: yt-aTeam04.mp4
    71        ${SH} ffmpeg -i $< -ss 243 -t 5 -vf scale=486:360 -r 25/1 -strict -2 $@
    72   
    73    at-clip19.mp4: yt-aTeam05.mp4
    74        ${SH} ffmpeg -i $< -ss 112 -t 5 -vf scale=486:360 -r 25/1 -strict -2 $@
    75   
    76    at-clip20.mp4: yt-aTeam02.mp4
    77        ${SH} ffmpeg -y -i $< -ss 81 -t 5 -strict -2 temp.$@
    78        ${SH} ffmpeg -i temp.$@ -filter:v "setpts=2.5*PTS" -strict -2 $@
    79   
    80    aTeam.mp3: yt-aTeam00.mp4
    81        ${SH} ffmpeg -y -i $< -vn -t 75 -strict -2 $@
    82   
    83    at-videoAn.mp4: at-clip01.mp4 at-clip02.mp4 at-clip03.mp4 at-clip04.mp4 at-clip05.mp4 at-clip06.mp4 at-clip07.mp4 at-clip08.mp4 at-clip09.mp4 at-clip10.mp4 at-clip11.mp4 at-clip12.mp4 at-clip13.mp4 at-clip14.mp4 at-clip15.mp4 at-clip16.mp4 at-clip17.mp4 at-clip18.mp4 at-clip19.mp4 at-clip20.mp4
    84        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    85        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    86        ${RM} $@.txt
    87   
    88    aTeam.mp4: at-videoAn.mp4 aTeam.mp3
    89        ${SH} ffmpeg -y -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    90   
$ cat -n makefile.fallguy
     1    yt-fallguy01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=iNizcrfnQ4E
     3   
     4    yt-fallguy02.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=0dxtiOqK8qg
     6   
     7    yt-fallguy03.mp4:
     8        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=dj9ev40C30M
     9   
    10    yt-fallguy04.mp4:
    11        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=s1uhbxz_Xfc
    12   
    13    yt-fallguy05.mp4:
    14        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=atZtkZdIWeM
    15   
    16    fg-clip01.mp4: yt-fallguy01.mp4
    17        ${SH} ffmpeg -i $< -ss 0 -t 4 -strict -2 $@
    18   
    19    fg-clip02.mp4: yt-fallguy02.mp4
    20        ${SH} ffmpeg -i $< -ss 61 -t 3 -strict -2 $@
    21   
    22    fg-clip03.mp4: yt-fallguy02.mp4
    23        ${SH} ffmpeg -i $< -ss 81 -t 3 -strict -2 $@
    24   
    25    fg-clip04.mp4: yt-fallguy02.mp4
    26        ${SH} ffmpeg -i $< -ss 101 -t 3 -strict -2 $@
    27   
    28    fg-clip05.mp4: yt-fallguy02.mp4
    29        ${SH} ffmpeg -i $< -ss 217 -t 5 -strict -2 $@
    30   
    31    fg-clip06.mp4: yt-fallguy02.mp4
    32        ${SH} ffmpeg -i $< -ss 248 -t 5 -strict -2 $@
    33   
    34    fg-clip07.mp4: yt-fallguy02.mp4
    35        ${SH} ffmpeg -i $< -ss 289 -t 3 -strict -2 $@
    36   
    37    fg-clip08.mp4: yt-fallguy02.mp4
    38        ${SH} ffmpeg -i $< -ss 5 -t 9 -strict -2 $@
    39   
    40    fg-clip09.mp4: yt-fallguy02.mp4
    41        ${SH} ffmpeg -i $< -ss 17 -t 9 -strict -2 $@
    42   
    43    fg-clip10.mp4: yt-fallguy02.mp4
    44        ${SH} ffmpeg -i $< -ss 28.5 -t 5.5 -strict -2 $@
    45   
    46    fg-clip11.mp4: yt-fallguy02.mp4
    47        ${SH} ffmpeg -i $< -ss 38.5 -t 1.5 -strict -2 $@
    48   
    49    fg-clip12.mp4: yt-fallguy02.mp4
    50        ${SH} ffmpeg -i $< -ss 42 -t 2.5 -strict -2 $@
    51   
    52    fg-clip13.mp4: yt-fallguy02.mp4
    53        ${SH} ffmpeg -i $< -ss 46.5 -t 5.5 -strict -2 $@
    54   
    55    fg-clip14.mp4: yt-fallguy02.mp4
    56        ${SH} ffmpeg -i $< -ss 54 -t 6 -strict -2 $@
    57   
    58    fg-clip15.mp4: yt-fallguy02.mp4
    59        ${SH} ffmpeg -i $< -ss 62 -t 3 -strict -2 $@
    60   
    61    fg-clip16.mp4: yt-fallguy02.mp4
    62        ${SH} ffmpeg -i $< -ss 66 -t 36 -strict -2 $@
    63   
    64    fg-clip17.mp4: yt-fallguy02.mp4
    65        ${SH} ffmpeg -i $< -ss 106 -t 24 -strict -2 $@
    66   
    67    fg-clip18.mp4: yt-fallguy02.mp4
    68        ${SH} ffmpeg -i $< -ss 137 -t 12 -strict -2 $@
    69   
    70    fg-clip19.mp4: yt-fallguy02.mp4
    71        ${SH} ffmpeg -i $< -ss 151 -t 19 -strict -2 $@
    72   
    73    fg-clip20.mp4: yt-fallguy02.mp4
    74        ${SH} ffmpeg -i $< -ss 178 -t 2 -strict -2 $@
    75   
    76    fg-clip21.mp4: yt-fallguy02.mp4
    77    #    ${SH} ffmpeg -i $< -ss 188 -t 222 -strict -2 $@
    78    #    ${SH} ffmpeg -i $< -ss 188 -t 165 -strict -2 $@
    79        ${SH} ffmpeg -i $< -ss 188 -t 23 -strict -2 $@
    80   
    81    fallGuy.mp3: yt-fallguy01.mp4
    82        ${SH} ffmpeg -i $< -ss 0 -t 106 -vn -strict -2 $@
    83   
    84    fg-videoAn.mp4: fg-clip01.mp4 fg-clip02.mp4 fg-clip03.mp4 fg-clip04.mp4 fg-clip05.mp4 fg-clip06.mp4 fg-clip07.mp4 fg-clip08.mp4 fg-clip09.mp4 fg-clip10.mp4 fg-clip11.mp4 fg-clip12.mp4 fg-clip13.mp4 fg-clip14.mp4 fg-clip15.mp4 fg-clip16.mp4 fg-clip17.mp4 fg-clip18.mp4 fg-clip19.mp4 fg-clip20.mp4 fg-clip21.mp4
    85        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    86        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    87        ${RM} $@.txt
    88   
    89    fallGuy.mp4: fg-videoAn.mp4 fallGuy.mp3
    90        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    91   
$ cat -n makefile.knightrider
     1    yt-knightrider01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=oNyXYPhnUIs
     3   
     4    yt-knightrider02.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://youtu.be/hfRiedxPQhs
     6   
     7    yt-knightrider03.mp4:
     8        ${SH} youtube-dl -f mp4 -o $@ https://youtu.be/LcG5jQPrOOQ
     9   
    10    yt-knightrider04.mp4:
    11        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=FBQ0PSxPRjc
    12   
    13    yt-knightrider05.mp4:
    14        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=rkBcGP256Ng
    15   
    16    yt-knightrider06.mp4:
    17        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=lVElVZlnTG0
    18   
    19    yt-knightrider07.mp4:
    20        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=mWDZsKSGoVg
    21   
    22    kr-clip01.mp4: yt-knightrider01.mp4
    23        ${SH} ffmpeg -i $< -ss 4 -t 27 -target ntsc-dvd $@
    24   
    25    kr-clip02.mp4: yt-knightrider01.mp4
    26        ${SH} ffmpeg -i $< -ss 53 -t 7 -target ntsc-dvd $@
    27   
    28    kr-clip03.mp4: yt-knightrider01.mp4
    29        ${SH} ffmpeg -i $< -ss 65 -t 0.5 -target ntsc-dvd $@
    30   
    31    #kr-clip04.mp4: yt-knightrider01.mp4
    32    #    ${SH} ffmpeg -i $< -ss 67 -t 10 -target ntsc-dvd $@
    33   
    34   
    35    knightRider.mp3: yt-knightrider01.mp4
    36        ${SH} ffmpeg -i $< -ss 4 -t 76 -vn $@
    37   
    38    kr-clip04.mp4: yt-knightrider02.mp4
    39        ${SH} ffmpeg -i $< -ss 285 -t 1 -target ntsc-dvd $@
    40   
    41    kr-clip05.mp4: yt-knightrider02.mp4
    42        ${SH} ffmpeg -i $< -ss 295 -t 4 -target ntsc-dvd $@
    43   
    44    kr-clip06.mp4: yt-knightrider02.mp4
    45        ${SH} ffmpeg -i $< -ss 315 -t 1 -target ntsc-dvd $@
    46   
    47    kr-clip07.mp4: yt-knightrider02.mp4
    48        ${SH} ffmpeg -i $< -ss 330 -t 10 -target ntsc-dvd $@
    49   
    50    kr-clip08.mp4: yt-knightrider02.mp4
    51        ${SH} ffmpeg -i $< -ss 347 -t 3 -target ntsc-dvd $@
    52   
    53    kr-clip09.mp4: yt-knightrider02.mp4
    54        ${SH} ffmpeg -i $< -ss 352 -t 5 -target ntsc-dvd $@
    55   
    56    kr-clip10.mp4: yt-knightrider02.mp4
    57        ${SH} ffmpeg -i $< -ss 362 -t 5 -target ntsc-dvd $@
    58   
    59    kr-clip11.mp4: yt-knightrider01.mp4
    60        ${SH} ffmpeg -i $< -ss 67 -t 10 -target ntsc-dvd $@
    61   
    62    kr-clip12.mp4: yt-knightrider06.mp4
    63        ${SH} ffmpeg -i $< -ss 4.2 -t 10 -target ntsc-dvd $@
    64   
    65   
    66    kr-videoAn.mp4: kr-clip01.mp4 kr-clip02.mp4 kr-clip03.mp4 kr-clip04.mp4 kr-clip05.mp4 kr-clip06.mp4 kr-clip07.mp4 kr-clip08.mp4 kr-clip12.mp4 kr-clip11.mp4
    67        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    68        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    69        ${RM} $@.txt
    70        ${SH} mplayer $@
    71   
    72   
    73    knightRider.mp4: kr-videoAn.mp4 knightRider.mp3
    74        ${SH} ffmpeg -y -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    75   
    76   
$ cat -n makefile.airwolf
     1    yt-airwolf01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=B10AXxnZGto
     3   
     4    yt-airwolf02.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=55VuB724n6Y
     6   
     7    yt-airwolf03.mp4:
     8        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=dnHY4rhNRKU
     9   
    10    airwolf.mp3: yt-airwolf01.mp4
    11        ${SH} ffmpeg -i $< -vn -strict -2 $@
    12   
    13   
    14    aw-clip01.mp4: yt-airwolf01.mp4
    15        ${SH} ffmpeg -i $< -ss 1 -t 39 -strict -2 -target ntsc-dvd $@
    16   
    17    aw-clip02.mp4: yt-airwolf01.mp4
    18        ${SH} ffmpeg -i $< -ss 74 -t 6 -strict -2 -target ntsc-dvd $@
    19   
    20    aw-clip03.mp4: yt-airwolf02.mp4
    21        ${SH} ffmpeg -i $< -ss 28 -t 12 -strict -2 -target ntsc-dvd $@
    22   
    23    aw-clip04.mp4: yt-airwolf02.mp4
    24    #    ${SH} ffmpeg -i $< -ss 130 -t 50 -strict -2 -target ntsc-dvd $@
    25        ${SH} ffmpeg -i $< -ss 130 -t 48 -strict -2 -target ntsc-dvd $@
    26   
    27    aw-clip05.mp4: yt-airwolf02.mp4
    28        ${SH} ffmpeg -i $< -ss 201 -t 2 -strict -2 -target ntsc-dvd $@
    29   
    30    aw-clip06.mp4: yt-airwolf02.mp4
    31        ${SH} ffmpeg -i $< -ss 209 -t 19 -strict -2 -target ntsc-dvd $@
    32   
    33    aw-clip07.mp4: yt-airwolf02.mp4
    34        ${SH} ffmpeg -i $< -ss 240 -t 4 -strict -2 -target ntsc-dvd $@
    35   
    36    aw-clip08.mp4: yt-airwolf02.mp4
    37        ${SH} ffmpeg -i $< -ss 254 -t 4 -strict -2 -target ntsc-dvd $@
    38   
    39    aw-clip09.mp4: yt-airwolf02.mp4
    40        ${SH} ffmpeg -i $< -ss 265 -t 3 -strict -2 -target ntsc-dvd $@
    41   
    42    aw-clip10.mp4: yt-airwolf02.mp4
    43        ${SH} ffmpeg -i $< -ss 550 -t 10 -strict -2 -target ntsc-dvd $@
    44   
    45    aw-clip11.mp4: yt-airwolf02.mp4
    46        ${SH} ffmpeg -i $< -ss 735 -t 40 -strict -2 -target ntsc-dvd $@
    47   
    48    aw-videoAn.mp4: aw-clip01.mp4 aw-clip04.mp4
    49        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    50        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    51        ${RM} $@.txt
    52   
    53    airwolf.mp4: aw-videoAn.mp4 airwolf.mp3
    54        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    55   
$ cat -n makefile.dukes
     1    yt-dukes01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=67gig0f4HLo
     3   
     4    yt-dukes02.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=2iMBQ-bXQIE
     6   
     7    yt-dukes03.mp4:
     8        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=pWuVWNSNWnM
     9   
    10    dukes.mp3: yt-dukes01.mp4
    11        ${SH} ffmpeg -i $< -vn -t 61 -strict -2 $@
    12   
    13    dukes.mp4: dh-videoAn.mp4 dukes.mp3
    14        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    15   
    16    dh-clip01.mp4: yt-dukes01.mp4
    17        ${SH} ffmpeg -i $< -ss 3 -t 7 -strict -2 $@
    18        ${SH} mplayer $@
    19   
    20    dh-clip02.mp4: yt-dukes01.mp4
    21        ${SH} ffmpeg -i $< -ss 15 -t 1.5 -strict -2 $@
    22        ${SH} mplayer $@
    23   
    24    dh-clip03.mp4: yt-dukes01.mp4
    25        ${SH} ffmpeg -i $< -ss 52.5 -t 8 -strict -2 $@
    26        ${SH} mplayer $@
    27   
    28    dh-clip04.mp4: yt-dukes02.mp4
    29        ${SH} ffmpeg -i $< -ss 94.5 -t 4 -strict -2 $@
    30        ${SH} mplayer $@
    31   
    32    #dh-clip05.mp4: yt-dukes02.mp4
    33    #    ${SH} ffmpeg -i $< -ss 177.5 -t 4.5 -strict -2 $@
    34    #    ${SH} mplayer $@
    35   
    36    dh-clip06.mp4: yt-dukes02.mp4
    37        ${SH} ffmpeg -i $< -ss 212 -t 3.5 -strict -2 $@
    38        ${SH} mplayer $@
    39   
    40    dh-clip07.mp4: yt-dukes02.mp4
    41        ${SH} ffmpeg -i $< -ss 561.5 -t 10 -strict -2 $@
    42        ${SH} mplayer $@
    43   
    44    dh-clip08.mp4: yt-dukes03.mp4
    45        ${SH} ffmpeg -i $< -ss 18 -t 3.25 -strict -2 $@
    46        ${SH} mplayer $@
    47   
    48    dh-clip09.mp4: yt-dukes03.mp4
    49        ${SH} ffmpeg -i $< -ss 24 -t 2.5 -strict -2 $@
    50        ${SH} mplayer $@
    51   
    52    #dh-clip10.mp4: yt-dukes03.mp4
    53    #    ${SH} ffmpeg -i $< -ss 29 -t 13 -strict -2 $@
    54    #    ${SH} mplayer $@
    55   
    56    dh-clip11.mp4: yt-dukes03.mp4
    57        ${SH} ffmpeg -i $< -ss 68 -t 5.5 -strict -2 $@
    58        ${SH} mplayer $@
    59   
    60    dh-clip12.mp4: yt-dukes03.mp4
    61        ${SH} ffmpeg -i $< -ss 78 -t 13 -strict -2 $@
    62        ${SH} mplayer $@
    63   
    64    dh-videoAn.mp4: dh-clip01-scaled.mp4 dh-clip02-scaled.mp4 dh-clip04-scaled.mp4 dh-clip06-scaled.mp4 dh-clip07-scaled.mp4 dh-clip08-scaled.mp4 dh-clip09-scaled.mp4 dh-clip11-scaled.mp4 dh-clip12-scaled.mp4 dh-clip03-scaled.mp4
    65        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    66        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    67        ${RM} $@.txt
    68   
$ cat -n makefile.magnum
     1    yt-magnum01.mp4:
     2        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=xIaXl7SqkBw
     3   
     4    yt-magnum02.mp4:
     5        ${SH} youtube-dl -f mp4 -o $@ https://www.youtube.com/watch?v=CICbxj1PF74
     6   
     7    magnum.mp3: yt-magnum01.mp4
     8        ${SH} ffmpeg -i $< -vn -t 61 -strict -2 $@
     9   
    10    magnum.mp4: mp-videoAn.mp4 magnum.mp3
    11        ${SH} ffmpeg -i $< -i $(shell echo $^ | cut -f 2 -d ' ') -strict -2 $@
    12   
    13    mp-clip01.mp4: yt-magnum01.mp4
    14        ${SH} ffmpeg -i $< -ss 16 -t 4 -strict -2 $@
    15   
    16    mp-clip02.mp4: yt-magnum01.mp4
    17        ${SH} ffmpeg -i $< -ss 40.5 -t 1.5 -strict -2 $@
    18   
    19    #mp-clip03.mp4: yt-magnum02.mp4
    20    #    ${SH} ffmpeg -i $< -ss 0 -t 22 -strict -2 $@
    21   
    22    mp-clip04.mp4: yt-magnum02.mp4
    23        ${SH} ffmpeg -i $< -ss 24 -t 6 -strict -2 $@
    24   
    25    mp-clip05.mp4: yt-magnum02.mp4
    26        ${SH} ffmpeg -i $< -ss 32.5 -t 1 -strict -2 $@
    27   
    28    #mp-clip06.mp4: yt-magnum02.mp4
    29    #    ${SH} ffmpeg -i $< -ss 38.5 -t 2.5 -strict -2 $@
    30    #
    31    #mp-clip07.mp4: yt-magnum02.mp4
    32    #    ${SH} ffmpeg -i $< -ss 47 -t 2.5 -strict -2 $@
    33   
    34    #mp-clip08.mp4: yt-magnum02.mp4
    35    #    ${SH} ffmpeg -i $< -ss 58 -t 17 -strict -2 $@
    36    #
    37    #mp-clip09.mp4: yt-magnum02.mp4
    38    #    ${SH} ffmpeg -i $< -ss 77.5 -t 15 -strict -2 $@
    39   
    40    mp-clip10.mp4: yt-magnum02.mp4
    41        ${SH} ffmpeg -i $< -ss 101 -t 20 -strict -2 $@
    42   
    43    mp-clip11.mp4: yt-magnum02.mp4
    44        ${SH} ffmpeg -i $< -ss 123 -t 3 -strict -2 $@
    45   
    46    mp-clip12.mp4: yt-magnum02.mp4
    47        ${SH} ffmpeg -i $< -ss 127 -t 12 -strict -2 $@
    48   
    49    mp-videoAn.mp4: mp-clip01.mp4 mp-clip02.mp4 mp-clip04.mp4 mp-clip05.mp4 mp-clip10.mp4 mp-clip11.mp4 mp-clip12.mp4
    50        ${SH} for f in `echo $^`; do echo "file '$$f'" >> $@.txt; done;
    51        ${SH} ffmpeg -y -f concat -i $@.txt -an $@
    52        ${RM} $@.txt